01.26 SOLID

2023. 1. 26. 10:07개발일지

단일 책임 원칙

 

"어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다" - 로버트 C. 마틴

  • '하나의 객체는 단 하나의 책임을 가져야 한다'
  • 왜? 객체는 꼭 하나의 책임을 가져야 하는 걸까요?

 

단 하나의 책임을 갖는 객체

  • 예를 들어서 회사-개발자, 국방부-예비군, 여자친구-남자친구 이런식으로 개발자 클래스, 예비군 클래스, 남자친구 클래스와 같이 역할과 책임을 클래스별로 분리하여 연관이 있는 클래스와 각각 따로 관계를 맺는 것이 서로 영향을 미치지 않아야 합니다.

단일 책임 원칙을 위반한 계산기

  • Calculator에게 주어진 책임과 역할이 너무 큽니다.
  • 각각의 연산자의 연산 과정에 변화가 생겼을 때 Calculate 메서드의 크기가 비대해 지거나 다른 연산 과정에 영향을 미칠 수 있습니다.

 

 

개방-폐쇄 원칙

 

"소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다." -로버트 C. 마틴
('자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다.')

 

 

Java의 Database 연결

  • Java에는 데이터베이스(Database)에 접속 할 수 있도록 도와주는 JDBC라는 API가 존재하며, 이 JDBC를 개방-폐쇄 원칙의 예로 들 수 있습니다.
  • 개발을 할 때 Oracle, MySQL 등 다양한 데이터베이스를 사용하게 되며, 이때 만약 개발이 진행되는 과정에서 데이터베이스(Database)를 변경하게 된다고 하더라도 우리가 그동안 개발하던 코드를 전부 수정할 필요는 없습니다.
  • 연결할 때 필요한 ID, PWD, URL 등의 설정을 담당하는 부분만 변경하면 쉽게 데이터베이스를 변경할 수 있습니다.

개방-폐쇄 원칙을 위반한 계산기

  • 연산자를 추가하려고 할 때 코드의 변화가 너무 많이 일어납니다.
  • if else 조건문 블록이 존재하며 연산자 추가 시 코드변화가 빈번하게 일어납니다.

 

원칙 적용 방법

  • 변화되는 부분을 추상화해서 변화를 고정 시키며 기능이 추가될 때 클래스의 상속을 통해서 하위 클래스에서 기능을  구현 하도록 합니다.
  • 기존코드를 수정하지 않아도 객체 상속의 다향성 원리에 의해 기능이 확장 되도록 합니다.
  • 연산 클래스는 추상화된 부모 클래스를 상속받아 기능별로 구현합니다.

AbstractOperation : 추상 클래스

  • operate : 추상 메서드

 

리스코프 치환 원칙

 

 

"서브 타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다." - 로버트 C. 마틴

  • '하위 클래스의 인스턴스는 상위형 객체 참조변수에 대입해 상위 클래스의 인스턴스 역할을 하는 데 문제가 없어야 한다'
  • 리스코프 치환 원칙 : 치환성(substitutability)은 객체 지향 프로그래밍 원칙이며, 컴퓨터 프로그램에서 자료형 S가 자료형 T의 서브타입이라면 필요한 프로그램의 속성(정확성, 수행하는 업무 등)의 변경 없이 자료형 T의 객체를 자료형 S의 객체로 교체(치환)할 수 있어야 한다는 원칙

 

원칙 위반 증상

  • 객체의 타입을 확인합니다.(instanceof)
  • 자식 클래스명이 연관되거나 의존성이 있는 클래스에서 자주 발생합니다.

리스코프 치환 원칙을 위반한 계산기

  • 추가적인 기능이 필요한 자식 클래스가 존재하기 때문에 타입 확인을 진행하고 있습니다.
  • 따라서 자식 클래스의 인스턴스가 부모클래스의 인스턴스 역할을 수행하는데 문제가 없다고 할 수 없습니다.

 

원칙 적용 방법

  • 부모와 자식 클래스 사이의 행위가 일관성이 있도록 추상화를 좀 더 정교하게 구현합니다.
  • 연산 기능을 추상화한 부모 클래스에 피연산자 값의 유효성 검사를 진행하는 메서드를 추가해 줍니다.
  • 계산기 클래스에서는 이 메서드를 사용하는 유효성 검사를 진행하고 이 유효성 검사가 필요한 자식 클래스에서는 이 추가된 유효성 검사 조건을 구체화합니다.

 

인터페이스 분리 원칙

"클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다." - 로버트 C. 마틴

  • '특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다'
  • 클라이언트가 필요하지 않는 기능을 가진 인터페이스에 의존해서는 안되고 최대한 인터페이스를 작게 유지해야 합니다.

 

원칙 위반 증상

  • 필요하지 않은 기능을 강제로 구현해야하는 상황이 발생합니다.
  • 필요하지 않은 혹은 사용 못 하는 기능이 강제로 구현되어 사용하지 못하도록 예외처리를 해야하는 상황이 발생 할 수 있습니다.

인터페이스 분리 원칙을 위반한 계산기

  • 필요하지 않는 기능을 강제로 구현하고 있습니다.
  • 사용하지 못하도록 예외처리를 구현하고 있습니다.

원칙 적용 방법

  • 필요하지 않은 기능을 강제로 구현하지 않도록 인터페이스를 분리합니다
  • 연산 결과를 보여주는 방법마다 인터페이스를 구현합니다.

DisplayResult : 인터페이스, 연산결과만 출력

  • displayResult : 추상 메서드

DisplayWithOperator : 인터페이스, 연산과정 포함 출력

  • displayResultWithOperator : 추상 메서드

 

의존성 역전 원칙

 

"고차원 모듈은 저차원 모듈에 의존하면 안 된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야 한다."

"추상화된 거은 구체적인 것에 의존하면 안된다. 구체적인 것이 추상화된 것에 의존해야 한다."

"자주 변경되는 구체(Concrete) 클래스에 의존하지 마라" - 로버트 C. 마틴

  • 세부적인 사항은 추상화에 의존해야 합니다.
  • 의존성 역전 원칙을 적용한 대표적 사례로는 이전 OCP 에서 설명했던 JDBC에서 찾아볼 수 있습니다.
  • 또한 OCP에서 본 계산기 예제도 마찬가지입니다.

원칙 위반 증상

  • 저수준 모듈에서 변경이 발생되면 고수준 모듈에 수정사항이 발생합니다.

의존성 역전 원칙을 위반한 계산기

  • 계산기(고수준 모듈)가 개별 연산 클래스(저수준 모듈)을 포함하며 계산 기능을 구현할 때 연산 클래스에 의존하고 있습니다.

원칙 적용 방법

  • 고수준 모듈의 변화되는 부분을 추상화합니다.
  • 저수준 모듈을 추상화에 의존시킵니다.
  • 계산기 클래스에 추상화된 부모 클래스를 포함시킵니다.
  • 연산 클래스는 추상화된 부모 클래스를 상속받아 기능별로 구현합니다.

AbstractOperation : 추상 클래스

  • operate : 추상 메서드

 

'개발일지' 카테고리의 다른 글

01.28 TIL  (0) 2023.01.28
01.27 TIL  (1) 2023.01.27
01.25 객체 지향 프로그래밍(Object Oriented Programming)  (0) 2023.01.26
01.25 TIL  (0) 2023.01.25
01.19 TIL  (0) 2023.01.19