본문 바로가기
Java 웹 프로그래밍

객체지향 개발 5대 원리 (수정중)

by irerin07 2018. 12. 4.
728x90

TL:DR

The 5 SOLID Principles explained
  • S – Single-responsibility principle. 단일 책임의 원칙
  • O – Open-closed principle. 개방폐쇄의 원칙
  • L – Liskov substitution principle. 리스코브 치환의 원칙
  • I – Interface segregation principle. 인터페이스 분리의 원칙
  • D – Dependency Inversion Principle. 의존성 역전의 원칙


Single responsibility principle
A class should have one and only one reason to change, 
meaning that a class should have only one job.

단일 책임의 원칙은 클래스의 목적을 명확히 함으로써 구조가 난잡해지거나 수정 사항이 불필요하게 넓게 퍼지는 것을 예방하고 기능을 명확히 분리할 수 있게 한다.

다음과 같은 자동차 클래스가 있다고 생각해보자

자동차 클래스는 단일 책임 원칙을 지키지 않는 클래스로서 

관리관련 메서드와 (세차(), 타이어교체())

운전관련 메서드로 (앞으로(), 뒤로(), 멈추기()) 이루어져있다.


이런 경우 하나의 클래스에 두가지의 책임이 발생하여 클래스의 목적이 모호해지고 
후에 유지보수를 함에 있어서 고난을 겪을수도 있다.
또한 여럿이서 개발을 할 경우 생산성의 저하를 일으킬수도 있다.


이것을 각각의 책임을 지니는 두가지의 클래스로 나누어보았다.






Open-closed principle
Objects or entities should be open for extension, 
but closed for modification.
확장에는 열려있어야 하지만 기존 코드의 수정에는 닫혀있어야 한다.

예를 들어 각 부서에 직원을 배치해야 하는 프로그램을 짠다고 해보자.


직원  A는 A부서에서 A부서의 방식대로 닦고 쓸고 한다.

하지만 직원 B는 B부서에서 B부서의 방식대로 닦고 쓸고 한다.


이런식으로 직접적으로 각 부서클래스의 메소드를 호출하고 결합도가 높아진다면

확장성이 떨어지고 유지 보수 하기가 힘들어진다.

이와같이 새로운 직원과 부서를 필요할때마다 만들어 나간다면 확장성은 떨어지고 기존 코드에서 많은 수정이 발생하게 될것이다.



이처럼 수정을 하여 각 직원이 각 부서에 직접적으로 관계를 가지지 않게 바꾼다면

직원은 따로 코드의 수정 없이 다른 부서와의 연동에는 확장적이게 될 것이다.


각 부서 클래스는 부서 interface를 implement함으로써 추상메서드 닦다(), 쓸다()를 Override해서 자기 입맛에 맞게 메서드를 

재정의 하기만 하면 되는것이다.


즉 위와 같은식으로 개발을 하게 되면

부서는 부서를 확장시키기 쉬워지고(A부서, B부서, C부서....) 직원이 추가됨에 따라 기존 부서 코드에 변화가 생기는것을 막을 수 있다는 것이다.(A부서 방식의 닦기() 쓸기()) (B부서 방식의 닦기() 쓸기())



Liskov substitution principle

sub-types must be substitutable for their base types


자식 클래스는 언제나 부모 클래스를 대체할 수 있다.


상속관계간 is a 관계를 잘 지키거나

구현관계간 is able to 관계를 잘 지키면 된다.



그림을 예시로 들어보자.


니로는 SUV를 상속받고 SUV는 자동차를 상속받는 is a 관계이다


위 그림을 보고 우리는

1. 니로는 SUV다

2. SUV는 자동차다

라고 말 할수 있고 이를 코드로 변환하면


자동차 자동차1 = new Suv();

Suv suv1 = new 니로();

라고 할 수 있다는 것이다.


이 코드가 성립하려면 부모와 그 자식들이 존재 할 때 부모가 가진 메소드 이름과 갯수를 

자식 역시 동일하게 가지고 있어야 한다는 뜻이다




Interface segregation principle.

Classes that implement interfaces, should not be forced to implement methods they do not use.



인터페이스를 사용하는 클래스에서 인터페이스에는 구현되어 있지만 클래스내부에서는 사용하지 않는 메소드를 

구현하도록 강요하면 안된다는 것이다.


예를 들어 도형이라는 interface가 있다고 해보자

이 interface에는 도형들의 넓이와 부피를 구할 수 있는 기능을 선언하고 있어서

이를 사용하여 원기둥의 넓이와 부피 그리고 정사각형의 넓이를 구하고 싶다고 하자.


이때 원기둥의 경우는 interface에서 선언한 두가지 기능을 모두 구현하는데 문제가 없다.

원기둥은 넓이와 부피를 모두 계산 할 수 있기 때문이다.


하지만 문제는 정사각형이다.

높이가 없는 평평한 정사각형은 부피를 구할 필요가 없기 때문이다.

이런 경우 도형 interface는 정사각형 클래스에 필요하지도 않은 부피계산 메소드를 구현하라고 강요하게 된다.


ISP를 따르고자 한다면

위와 같은 경우에는 평평한도형 이라는 interface를 따로 만들어서 넓이만 구하는 기능을 선언하여

정사각형 클래스에서 불필요한 기능이 생성되는걸 막아주면 된다.


다만 대부분의 경우 ISP보다는 SRP를 사용하는것이 더 좋은 해결법이라고 한다.







Dependency Inversion Principle

High level modules should not depend on low level modules rather both should depend on abstraction. Abstraction should not depend on details; rather detail should depend on abstraction.


변화하기 쉽고 구체적인 하위클래스가 아닌

변하지 않을 가능성이 높고 추상적인 상위클래스(Interface와 같은)에 의존하라는 원칙이다.


예를들어 아우디 클래스가 하위 클래스인 스노우 타이어 클래스에 직접적으로 의존한다고 생각하보자

스노우 타이어는 사실 눈이 오는 겨울이 지나면 쓸모가 없으므로 겨울이 지나면 타이어를 교체하는것이 일반적이다.

이런 경우 아우디는 자신보다 더 변화에 민감한 스노우 타이어에 의존을 하게 된다.


이 경우 OCP에서 처럼 아우디를 타이어라는 인터페이스에 연결하고

타이어 인터페이스로 여러 종류의 타이어를 구현하면 된다.


이렇게 구현을 할 경우 아무리 아우디가 타이어를 교체한다고 하더라도

아우디는 타이어 인터페이스에만 의존하고 있기 때문에 타이어의 변화에 영향을 받지 않게 된다.


이처럼 변하지 않을 가능성이 높고 추상적인 상위 클래스에 의존하도록 하여 변화에 영향을 받지 않도록 하는것이다.

















728x90