- 추상화
- 캡슐화
- 다형성
- 상속
- 바뀌는 부분은 캡슐화한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
- 인터페이스라고해서 클래스를 선언하는 부분에서 implements 키워드를 써서 어떤 자바 인터페이스를 구현하는 클래스를 만든는게 아니다.
- 일반적으로 어떤 상위 형식(클래스, 인터페이스 등)에 있는 메소드를 구현하는 구상 클래스는 그 상위 형식의 인터페이스를 구현하는 클래스라고 생각하면 된다.
- 애플리케이션에서 달라지는 부분을 찾아 내고, 달라지지 않는 부분으로부터 분리 시킨다.
- 달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 "캡슐화" 한다.
- 그러면 코드를 변경하는 과정에서 의도하지 않은 일이 일어나는 것을 줄이면서 시스템의 유연성은 향상시킬 수 있다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
- 실제 실행시에 쓰이는 객체가 코드에 의해서 고정되지 않도록, 어떤 상위(supertype)에 맞춰서 프로그래밍함으로써 다형성을 활용해야한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할 수 있다.
- 객체 사이의 상호의존성을 최소화할 수 있기 때문이다.
- OCP(Open-Closed Principle)
- 클래스는 확장에 대해서는 열려있어야 하지만 코드 변경에 대해서는 닫혀있어야 한다.
- 이 규칙을 잘 지키면 급변하는 주변 환경에 잘 적응할 수 있으면서도 강하고 튼튼한 디자인을 만들 수 있다.
- DIP(Dependency Inversion Principle) 의존성 뒤집기 원칙
- 추상화된 것에 의존하도록 만들어야한다.
- 구상 클래스에 의존하도록 만들지 않도록 한다.
- 이 원칙은 고수준 구성요소가 저수준 구성요소에 의존하면 안 된다는 것이 내포되어있다.
- 추상화된 것에 의존하라
- 추상화된 것에 의존하라, 구상 클래스에 의존하지 않도록 한다.
- 어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 말아야 한다.
- new 연산자를 사용하면 구상 클래스에 대한 레퍼런스를 사용하게되는 것이다.
- 팩토리를 써서 구상 클래스에 대한 레퍼런스를 변수에 저장하는 일을 미리 방지한다.
- 구상 클래스에서 유도된 클래스를 만들지 말아야 한다.
- 구상 클래스에서 유도된 클래스를 만들면 특정 구상 클래스에 의존하게 된다.
- 인터페이스나 추상 클래스처럼 추상화된 것으로부터 클래스를 만들어야 한다.
- 베이스 클래스에 이미 구현되어있던 메소드를 오버라이드하지 말아야 한다.
- 이미 구현되어있는 메소드를 오버라이드 한다는 것은 애초부터 베이스 클래스가 제대로 추상화 될 것이 아니었다고 볼 수 있다.
- 베이스 클래스에서 메소드를 정의할 때는 모든 서브 클래스에서 공유할 수 있는 것만 정의해야 한다.
- 정말 친한 치구하고 얘기하고, 낯선이에게 메세지를 말하지 마라
- 데메테르의 법칙은 간단하게 의존성을 줄이는 것이다.
// 원칙을 따르지 않는 경우
class Test {
public float getTemp() {
Thermometer thermometer = sation.getThermometer();
return thermometer.getTemperature();
}
}
// 원칙을 따른 경우
class Test2 {
public float getTemp() {
return station.getTemperature();
}
}
- 첫번 쨰는 의존하는 클래스 객체를 두개를 의존한다. 만약에 thermometer가 바뀌게 되는 경우 Test Class 또한 변하겐된다.
- 두번 째는 의존하는 클래스 객체가 한개이며 원칙을 잘 지킨 케이스이다.
- 객체 자체의 메서드들
- 메서드에 매개변수로 전달된 객체
- 그 메서드에 생성하거나 인스턴스를 만든 객체
- 그 객체에 구성하는 구성요소
public class Car {
// 구성 요소의 메서드는 호출 가능
Engine engine;
public Car() {
// 엔진 초기화 등을 처리
}
public void start(key Key) {
// 새로운 객체를 생성 이 객체의 메서드를 호출 가능
Doors doors = new Doors();
// 매개변수로 전달된 객체의 메서드 호출 가능
boolean authorized = key.turnes();
if(authorized) {
// 객체의 구성요소의 메서드는 호출 가능
engine.start();
// 객체 내에 있는 메서드는 호출 가능
updateDashboardDisplay();
// 인스턴스를 만든 객체의 메서드는 호출 가능
doors.lock();
}
}
public void updateDashboardDisplay() {
// 디스플레이 갱신
}
}
- Java의 System.out.println 데메테르의 법칙에 어긋난다.
- "먼저 연락하지 마세요. 저희가 연락 드리겠습니다."
- 고수준 구성요소가 저수준 구성요소에 의존하고 그 저수준 구성요소가 다시 고수준 구성요소에 의존하면서 "의존성 부패(dependency rot)"가 발생한다.
- 헐리우드 원칙을 활용하면 해당 의존성 부패를 방지가 가능하다.
- 즉 고수준 구성요소에서 저수준 구성요소에게 "먼저 연락하지 말아라, 제가 먼저 연락 드리겠다"라고 얘기하는 것과 같다.
- 사실 저수준 구성요소에서도 상속 계층구조상 위에 있는 클래스에서 정의한 메서드를 상속을 통해서 호출하게되는 경우가 빈번하지만 저수준과 고수준 사이에 확연하게 드러나는 순환 의존성이 생기는 것은 파하는게 좋다.
- "클래스를 바꾸는 이유는 한 가지 뿐이어야 한다."
- 어떤 클래스에서 맡고 있는 모든 역할들은 나중에 코드 변화를 불러올 수 있다.
- 역할이 두개 이상 있으면 바뀔 수 있는 부분이 두가지 이상이 되는 것이다.
- 해당 원칙에 따라 한 클래스에서는 한 가지 역할만 맡도록 해야 한다.
- 한 클래스 또는 모듈이 특정 목적 또는 역할을 얼마나 일관되게 지원하는지를 나타내는 척도라고 할 수 있다.
- 어떤 모듈 또는 클래스의 응집도가 높다는 것은 이련의 서로 연관되어 기능이 묶여있다는 것
- 응집도가 낮다는 것은 서로 상관 없는 기능들이 묶여있다는 것을 뜻한다.
- 즉 코드에 새로운 요구사항이 있을 때 마다 바뀌는 부분이 있다면 그 행동을 바뀌지 않는 다른 부분으로부터 골라내서 분리해야 한다.
- 정리 => 바뀌는 부분은 따로 뽑아서 캡슐화, 그렇게하면 나중에 바뀌지 않는 부분에는
- 영향을 미치지 않은 채로 그 부분만 고치거나 확장할 수 있다.