JUINTINATION
객체 지향 원리 본문
반응형
자바 언어의 가장 큰 특징은 객체 지향 언어, 즉 객체 지향 프로그래밍에서 사용하는 언어라는 점이다.
- 객체 지향 프로그래밍은 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다.
- 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만든다.
객체 지향 언어의 특징
- 추상화
- 집합을 구성하는 개체들을 '일반화'하는 것
- 어떤 영역에서 필요로 하는 속성이나 행위를 추출하는 작업
- 관심 있는 부분에 더욱 집중할 수 있음
switch(자동차 종류) { case 아우디: // 아우디 엔진 오일 교환 코드 case 벤츠: // 벤츠 엔진 오일 교환 코드 } // 자동차 종류가 추가될 때마다 위의 코드를 수정하지 않고 아래와 같이 해결 void changeEngineOil(Car c) {} c.changeEngineOil(); }
- 집합을 구성하는 개체들을 '일반화'하는 것
- 캡슐화
- 객체의 필드과 메서드를 하나로 묶고 실제 구현 내용 일부를 내부에 감추어 은닉
- 코드의 결합도를 낮추고 응집도를 높히며 설계
- 요구사항 변경에 영향을 최소화하여 유연하게 대처가 가능하게 함
- ex) getter&setter 메서드는 필드를 캡슐화한 것임
public class ArrayStack { public int top; public int[] itemArray; public int stackSize; public ArrayStack(int n) { itemArray = new int[n]; stackSize = 0; top = -1; } } public class StackClient { public static void main(String[] args) { ArrayStack st = new ArrayStack(10); st.itemArray[++st.top] = 20; System.out.print(st.itemArray[st.top]); } } // 위의 코드를 캡슐화한 코드는 아래와 같음 public class ArrayStack { private int top; private int[] itemArray; private int stackSize; public ArrayStack(int n) { itemArray = new int[n]; stackSize = 0; top = -1; } public void push(int i) { itemArray[++top] = i; } public int peek() { if (stackSize > 0) return itemArray[top]; else System.exit(1); } } public class StackClient { public static void main(String[] args) { ArrayStack st = new ArrayStack(10); st.push(20); System.out.print(st.peek()); } }
- 객체의 필드과 메서드를 하나로 묶고 실제 구현 내용 일부를 내부에 감추어 은닉
- 일반화(상속)
- 클래스 자체를 캡슐화
- 변경에 대비할 수 있는 설계를 가능하게 함
- 수퍼클래스, 또는 부모 클래스 등의 기존의 클래스로부터 속성과 동작을 상속
- 새로운 클래스가 추가되더라도 클라이언트는 영향을 받지 않음
while (장바구니에 과일이 있다) { switch (과일 종류) { case 사과: 가격 총합 += 사과 가격; case 배: 가격 총합 += 배 가격; } } // 과일 종류가 추가될 때마다 위의 코드를 수정하지 않고 아래와 같이 해결 int computeTotalPrice(List<Fruit> fruits) { int total = 0; for (Fruit fruit : fruits) { total += fruit.price; } return total; }
- 클래스 자체를 캡슐화
- 다형성
- 서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력
- 일반화 관계와 함께 자식 클래스를 개별적으로 다룰 필요 없이 한 번에 처리할 수 있게 하는 수단을 제공
- 부모 클래스의 참조 변수는 자식 클래스가 상속받은 부모 클래스의 변수와 메서드에 접근할 수 있음
- 코드를 간결하게 할 뿐 아니라 변화에도 유연하게 대처할 수 있음
public abstract class Pet { public abstract void talk(); } public class Cat extends Pet { public void talk() { System.out.println("야옹"); } } public class Dog extends Pet { public void talk() { System.out.println("멍멍"); } } public class Main { public static void main(String[] args) { Pet[] p = {new Cat(), new Dog()}; for (int i = 0; i < 2; i++) { p[i].talk(); } } }
- 서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력
일반화의 위임
어떤 클래스의 일부 기능만을 사용하고 싶을 경우에는 위임을 사용해야 한다.
두 자식 클래스 사이에 ‘is a kind of 관계’가 성립되지 않을 때 상속을 사용하면 불필요한 속성이나 연산(빚이라 해도 될 것이다)도 물려받게 된다.
예를 들어 다음과 같이 stack을 만들었다고 했을 때 push, pop 메서드를 통하지 않고 ArrayList에 직접 접근하여 값을 얻을 수 있기 때문에 스택의 무결성 조건인 LIFO를 위배한다.
class MyStack<E> extends ArrayList<E> {
public void push(E e) {
add(e);
}
public E pop() {
return remove(size() - 1);
}
}
일반화를 위임으로 변환하는 프로세스
- 자식 클래스에 부모 클래스의 인스턴스를 참조하는 속성을 만든다. 이 속성 필드를 this로 초기화한다.
class MyStack<E> extends ArrayList<E> {
private ArrayList<E> aList = this;
public void push(E e) {
add(e);
}
public E pop() {
return remove(size() - 1);
}
}
- 서브 클래스에 정의된 각 메서드에 1번에서 만든 위임 속성 필드를 참조하도록 변경한다.
class MyStack<E> extends ArrayList<E> {
private ArrayList<E> aList = this;
public void push(E e) {
aList.add(e);
}
public E pop() {
return aList.remove(aList.size() - 1);
}
}
- 서브 클래스에서 일반화 관계 선언을 제거하고 위임 속성 필드에 슈퍼 클래스의 객체를 생성해 대입한다.
class MyStack<E> {
private ArrayList<E> aList = new ArrayList<>();
public void push(E e) {
aList.add(e);
}
public E pop() {
return aList.remove(aList.size() - 1);
}
}
- 서브 클래스에서 사용된 슈퍼 클래스의 메서드에도 위임 메서드를 추가한다.
class MyStack<E> {
private ArrayList<E> aList = new ArrayList<>();
public void push(E e) {
aList.add(e);
}
public E pop() {
return aList.remove(aList.size() - 1);
}
public boolean isEmpty() {
return aList.isEmpty();
}
public int size() {
return aList.size();
}
}
- 컴파일하고 잘 동작하는지 확인한다.
특수화
- 일반화의 역관계, 즉 부모 클래스에서 자식 클래스를 추출하는 과정
- 특수화가 필요한 경우
어떤 속성이나 연관 관계가 특정 자식 클래스에서만 관련이 있고, 다른 자식 클래스에서는 관련이 없는 경우
- ex) VIP 멤버들에게만 할인 쿠폰이 발행된다.
피터 코드의 상속 규칙
- 자식 클래스와 부모 클래스 사이는 "역할 수행" 관계가 아니어야 한다.
- 한 클래스의 인스턴스는 다른 서브 클래스의 객체로 변환할 필요가 절대 없어야 한다.
- 자식 클래스가 부모 클래스의 책임을 무시하거나 재정의하지 않고 확장만 수행해야 한다.
- 자식 클래스가 단지 외부 기능을 재사용할 목적으로 유틸리티 역할의 클래스를 상속하지 않아야 한다.
- 자식 클래스가 역할(Role), 트랜잭션(Transaction), 디바이스(Device) 등을 특수화(specialization)해야 한다.
1번 규칙 위배
2번 규칙 위배
3번 규칙 점검 불가
4번 규칙 준수
5번 규칙 위배
위의 클래스 다이어그램을 다음과 같이 수정할 수 있다.
집약(혹은 연관) 관계를 사용하여 클래스 사이의 관계를 표현하여
어느 순간에는 두 역할 모두 수행하지 않을 수 있다는 다중성 또한 표현할 수 있다.
기존의 코드(사람)에 영향을 주지 않고 새로운 역할을 추가할 수 있다.
이는 SOLID 원칙에서 OCP를 준수한 것이다.
728x90
'JAVA객체지향디자인패턴' 카테고리의 다른 글
스테이트(State) 패턴 (0) | 2023.07.06 |
---|---|
스트래티지(Strategy) 패턴 (0) | 2023.07.06 |
싱글턴(Singleton) 패턴 (0) | 2023.07.05 |
디자인패턴 개요 (0) | 2023.07.03 |
좋은 객체 지향 설계의 5가지 원칙 (SOLID) (0) | 2023.07.02 |
Comments