불로구

객체지향이란? - solid 원칙 (2) 본문

프로그래밍/JAVA

객체지향이란? - solid 원칙 (2)

맹이맹이 2021. 4. 27. 02:09
반응형

SOLID 원칙

  • 소프트웨어를 설계함에 있어 이해하기 쉽고, 유연하며, 유지보수가 편하도록 도와주는 5가지 원칙
  • 단일책임원칙, 개방폐쇄원칙, 리스코프치환원칙, 인터페이스분리원칙, 의존성역전원칙

단일 책임 원칙 ( SRT, Single Responsibility Principle )

  • 모든 클래스는 단 한가지의 책임을 부여받아, 수정할 이유가 단 한 가지여야 한다.
  • 즉, 클래스에 속해있는 멤버들과 메소드는 모두 공통적으로 하나의 서비스를 위해 필요
  • 적용 방법
    • 각 책임을 각각의 개별 클래스로 분할
    • 분할 후에도 비슷한 책임을 갖고 있다면 부모클래스로 추출 , 필드나 메소드를 옮길 수도 있다.
    • 클래스 이름을 해당 클래스의 책임을 나타낼 수 있도록 작성
  • SRT 적용 전
class Car{
  private String serialNum;
  private String engine;
  private String color;
  private String model
  public Car(String serialNum, String engine, String color, String model){
    this.serialNum = serialNum;
    this.engine = engine;
    ...
  }
}
  • SRT 적용 후
class Car{
   private String serialNum;
   private CarSpec spec;
   public Car(String serialNum, CarSpec spec){
     this.serialNum = serialNum;
     this.spec = spec;
   }
 }
 
 class CarSpec{
   private String engine;
   private String color;
   private String model;
   public CarSpec(String engine, String color, String model){
    this.engine = engine;
    this.color = color;
    this.model = model;
   }
 }

-> 공통으로 쓸수있는 engine, color, model을 추출하여 CarSpec 클래스를 생성했다. -> 변경이 발생하더라도 Car 클래스를 수정할 필요가 없다.

개방 폐쇄 원칙 ( OCP, Open-Closed Principle )

  • 소프트웨어의 구성요소 (컴퍼넌트, 클래스, 모듈, 함수)가 확장에 대해 유연해야 하지만, 수정에는 폐쇄적이어야 한다.
  • 관리와 재사용이 가능한 코드를 만드는 기반
  • 개방 폐쇄 원칙 적용에 중요한 매커니즘은 추상화 , 다형성
  • 변경될 것과, 변경하지 않을 것을 구분하여 인터페이스를 정의하고, 구체적 타입 대신 인터페이스에 의존하도록 코드 작성
  • 상속보다는 포함 관계를 활용
  • OCP 적용 전
 class Car{
   private String serialNum;
   private CarSpec spec;
   public Car(String serialNum, CarSpec spec){
     this.serialNum = serialNum;
     this.spec = spec;
   }
 }
 
 class CarSpec{ ... }
 
 class AirPlane{
   private String serialNum;
   private AirPlaneSpec spec;
   public car(String serialNum, AirPlane spec){
    this.serialNum = serialNum;
    this.spec = spec;
   }
 }
 
 class AirPlaneSpec{ ... }
  • OCP 적용 후
 class Car extends Vehicle{
   private String serialNum;
   private CarSpec spec;
   public Car(String serialNum, CarSpec spec){
     this.serialNum = serialNum;
     this.spec = spec;
   }
 }
 class CarSpec extends VehicleSpec{ ... }
 
 class AirPlane extends Vehicle{ ... }
 class AirPlaneSpec extends VehicleSpec{ ... }

리스코프 치환 원칙

  • 상위 타입은 항상 하위 타입으로 대체할 수 있어야 한다.
  • 즉, 하위 객체에 접근할 때 그 상위 객체의 인터페이스로 접근하더라도 아무런 문제가 없이 일관성 있는 행동을 해야 한다.
  • 메소드의 사전 조건은 축소되지 않아야 하며, 사후 조건은 확대되지 않아야 한다.
  • 리스코프 치환 원칙을 통해 다형성 확장성을 극대화 가능
  • 객체 간 is-a 관계일 때만 상속 관계로 모델링 한다.
  • 하위 클래스의 공통된 연산을 인터페이스로 제공하고, 구분할 수 있는 멤버를 둔다.
  • 하위 클래스는 확장만 수행해야 하며, 상위 클래스의 책임을 무시할 수 없다.
  • 하위 클래스가 상속받은 기능 외 필요한게 있다면 구체화(implements) 이용
void f(){
  LinkedList list = new LinkedList();
  //...
  modify(list);
}
void modify(LinkedList list){
  list.add(...);
  doMethod(list);
}

-> LinkedList의 속도개선을 위해 HashSet을 사용한다면? -> LinkedList와 HashSet 모두 Collection Interface를 상속

void f(){
  Collection collection = new HashSet();
  //...
  modify(list);
}
void mofidy(Collection collection){
  collection.add(..);
  doMethod(collection);
}

-> 이렇게 Collection 생성 부분을 바꿔주면 어떤 컬렉션 클래스든 사용할 수 있다.

반응형
Comments