객체 지향적 설계 원칙 : SOLID

 

 

- 앞서 포스팅했던 객체 지향적 사고를 5가지 원칙으로 정의.

- 5가지 항목이 서로 연관되어있어 특정 원칙을 나눠 이해하기보단 종합적으로 이해하는 것이 도움이 됨.

 

 

(1) 단일 책임 원칙 (Single responsibility principle; SRP) | 클래스는 단 한 가지의 책임만을 가져야 한다.

 

* 미 적용 시 문제점 : 하나의 클래스가 여러가지 책임을 가지고 있다면, 수 많은 커스터마이징을 필요로 하는 유지보수 작업들을 처리할 때 이곳 저곳 소스를 헤집어 여러 군데를 수정해야 하는 상황을 초래할 수 있음.

 -> 비효율적인 커스터마이징 업무.

 

* 적용 방안 : 구현 할 당시에는 기능(함수)을 수행하는 역할 별로 세분화하여 코딩하고(ex, MVC), 각 기능에 상세한 명세를 주석으로 추가.

 

 

(2) 개방-폐쇄 원칙 (Open-closed principle; OCP) | 기능을 변경하거나 확장할 수 있으면서, 그 기능을 사용하는 코드는 수정하지 않는다.

 

* 미 적용 시 문제점

 1) 다운 캐스팅(이미 캐스팅된 객체를 다시 원래의 상태로 캐스팅하는 작업)을 하게됨.

 -> 캐스팅된 객체의 자료형이 모호해짐.

 2) 단위 별 수정 사항이 들어왔을 때 마다 실제 기능을 수행하는 함수에서의 수정이 불가피해짐.

 -> 수정되야할 부분이 중구난방식이 됨.

 

* 적용 방안 : 결국은 코드 수정의 유연함을 담아야 하기 때문에, 변화가 예상되는 기능을 추상화(오버로딩, 오버라이딩 등)하여야 함.

 

 

(3) 리스코프 치환 원칙 (Liskov substitution principle; LSP) | 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.

 

* 미 적용 시 문제점 : 상위 타입의 객체 자체가 가지고 있는 기능이 하위 타입으로 인해 변경되어 상위 타입의 객체를 사용하던 기능들이 전체적으로 변경되는 에러가 발생.

 -> 기능 변경에 있어 유연함이 떨어져 '개발-폐쇄 원칙'과 같이 중구난방적으로 수정이 필요한 상황을 초래함.

 

* 적용 방안 : 상위 타입의 객체를 사용하는 기능들을 확인하고나서 고정시켜야 할 공통적인 기능은 상위 타입에 세팅하고 나머지 세부적인 변경 사항들은 하위 타입을 여러 케이스로 만듦. 하위 타입의 세부적인 기능들을 세팅 할 하나의 기준을 잡아 상위 타입에 세팅하는 방법도 있음.

 

 

(4) 인터페이스 분리 원칙 (Interface segregation principle; ISP) | 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.

 

* 미 적용 시 문제점 : Java에선 .class 파일을 로딩하는 과정에 동적으로 링크 과정을 발생시키지만, C/C++ 에서는 링크시키는 과정을 컴파일 시 항상 거치기 때문에, 인터페이스가 분리되어 있지 않으면 하나의 인터페이스에 링크되어있는 소스들 전부가 재 컴파일을 해야하는 상황이 발생.

 -> 재 컴파일이 될 필요가 없는 클래스들의 지속적인 재 컴파일로 인한 컴파일 속도 이슈와 '단일 책임 원칙'을 거슬러 각 인터페이스의 책임에 대한 정의가 모호해짐.

 

* 적용 방안 : '단일 책임 원칙'을 인터페이스에 부여해 각 클래스(클라이언트)의 역활에 따라 인터페이스를 만들어줌.

 

 

(5) 의존 역전 원칙 (Dependency inversion principle; DIP) | 고 수준 모듈은 저 수준 모듈의 구현에 의존해서는 안 된다. 저 수준 모듈이 고 수준 모듈에서 정의한 추상 타입에 의존해야 한다.

 

* 미 적용 시 문제점 : 일정 수준 안정화가 된 프로그램에서의 수정에 있어서 고 수준 모듈(상위 타입, 큰 틀에서의 프로세스)은 최대한 변경을 피하고 저 수준 모듈(하위 타입, 실제 기능을 동작시키는 프로세스)을 변경하여 대처해야 '리스코프 치환 원칙'을 위배하는 경우를 줄이고 변경 범위가 작아질텐데 '의존 역전 원칙'을 어길 시, 개발 수정 범위가 다양한 범위로 늘어남.

 -> '리스코프 치환 원칙'과 마찬가지로 개발 수정 범위가 중구난방식으로 유연함이 떨어지고, 작은 단위의 수정 사항에도 패키지 단위로 확장되어 배포해야할 상황의 소프트웨어를 만들어내게 됨.

 

* 적용 방안 : '리스코프 치환 원칙'을 따라 설계하여 상위 타입은 수정없이 기능을 고정하되, 기능 변경에 대한 요청 사항이 있을 시 하위 타입의 수정을 통해 유연한 기능 변경을 할 수 있도록 추상화를 적절히 사용할 수 있도록 설계해야 함.

 

 

 

"코드 변화에 있어 유연한 대처가 가능하도록 설계를 해야한다." - SOLID 원칙

 

 

 

다음 포스트에서는 SOLID 원칙과 더불어 사용되는 DI와 서비스 로케이터에 대해 알아보도록 하자.

 

 

출처: 최범균, 『개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴』, 인투북스(2014)

'IT > Design Pattern' 카테고리의 다른 글

객체 지향(Object Oriented) 이란?  (0) 2018.09.18
디자인 패턴 (Design Pattern) 이란?  (0) 2018.09.17

객체 지향(Object Oriented) 이란?

 

1) 객체 | 기능(Operation)과 함수(Method)로 구성.

ex) 식당 주문 Process.

 

2) 인터페이스와 클래스 | 객체가 제공하는 기능(Operation)을 명세하는 인터페이스. 실제 객체의 구성의 집합은 클래스.

ex)

{매장 클래스에 명세된 Interface 목록}

 - 날짜 및 영수증 번호 등.

 - 결제 수단 집합 배열.

 - 주문을 받는 Method.

 - 결제 Method.

 - 상품을 건네주는 Method.

 

3) 메세지 | 클래스에서 인터페이스의 명령에 따라 객체의 기능(책임)을 전달하는 매개체.

ex)

 (1) 매장의 환경 설정 값을 확인한다.

 (2) 손님이 들어오면 주문을 받는다.

 (3) 해당 상품에 대해 결제를 진행한다.

 (4) 결제가 정상적으로 완료되면 상품을 제조하여 손님에게 건네준다.

 

4) 의존 | 각 객체가 서로를 생성하거나, 서로의 기능(Operation, Method)을 사용해야하는 흐름에 따른 이념.

ex)

{매장 클래스에서 발생된 결제에 대해서 산술 클래스에 의존하여 산술을 처리하는 방식}

...

#include "clsCalc.h"

...

double clsStore::CalcProcess(short snType) {

clsCalc objCalc = new clsCalc();

 

if(snType == CASH)

objCalc.CalcCash();

else if(snType == CARD)

objCalc.CalcCredit();

 

delete objCalc;

}

...

 

1. 캡슐화(Encapsulation) | 클래스를 모듈화하여 코드 은닉 및 내부 구조 변경의 유연성을 증진시키는 기법을 사용하는 과정.

ex) 공통 모듈화

 - 상품 결제 시 결제 금액, 부가세, 봉사료 등을 계산하는 클래스.

 - 매장 멤버십 고객 포인트 처리에 대해 계산하는 클래스.

 - 직원 출, 퇴근 관련 인사 관리를 담당하는 클래스.

 

** 데미테르의 법칙(Law of Demeter) 준수 **

 (1) 메서드에서 생성한 객체의 메서드만 호출.

 (2) 파라미터로 받은 객체의 메서드만 호출.

 (3) 필드로 참조하는 객체의 메서드만 호출.

 

2. 다형성(Polymorphism)과 상속(Inheritance)

[ 다형성 | 한 객체(혹은 메소드, 기능(Operation))를 재사용 필요에 의해 여러 타입으로 변경하여 사용하는 기법. ]

[ 상속 | 상속해주는 클래스(부모(super))가 상속받는 클래스(자식(parent))에게 자신의 기능(Operation)의 권한을 위임시켜주는 기법. ]

 -> 인터페이스 상속 | 추상 함수만을 가지는 추상 클래스를 상속시키는 기법.

 -> 구현(Class) 상속 | 기능 재사용을 목적으로 하는 일반 클래스를 상속시키는 기법.

 

ex)

 (1) 오버라이딩(Overriding)

  - 부모 클래스에게 상속받은 메소드를 자식 클래스에서 이름과 인자, 반환 값을 모두 같게 재 선언하되, 다른 기능(Operation)을 부여하는 기법.

 (2) 오버로딩(Overloading)

  - 부모 클래스에게 상속받은 메소드를 자식 클래스에서 이름은 같되 인자의 구조는 달라야하며, 반환 값은 제한받지 않는 방식으로 기능(Operation)을
부여하는 기법.

 

** 상속 기법의 사용에 있어 드러날 수 있는 문제점 **

 1) 부모 클래스의 변경으로 인해 해당 부모 클래스에게 상속을 받은 자식 클래스들의 수정이 불가피해질 수 있음.

 2) 다중 상속이 불가능한 환경에서는 클래스의 불 필요한 증가가 불가피해질 수 있음.

 3) 유사하지만 전혀 다른 기능을 가지는 각기 다른 클래스 구현 시 혼란을 야기시킬 수 있음.

 --> 상속보다는 각 클래스의 조립을 통해 기능을 엮는 방법도 고려하기에 충분하다.

 

3. 추상화 | 데이터나 프로세스 등을 의미가 비슷한 개념이나 표현으로 정의하는 과정.

ex)

 (1) 계산에 대한 로직 추상화

  - 현금, 카드 및 기타 결제 수단 관련 클래스.

  - 멤버십 포인트 사용 관련 클래스.

  - 할인 수단 관련 클래스.

 (2) 직원 인사에 대한 로직 추상화

  - 출, 퇴근 관련 클래스.

  - 각 직원 별 매출 집계 관련 클래스.

 

 

이제 다음 포스트에서 위의 객체 지향적 사고를 바탕으로 구성된 설계 원칙(SOLID) 기법에 대해 알아보도록 하자.

 

 

출처: 최범균, 『개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴』, 인투북스(2014)

'IT > Design Pattern' 카테고리의 다른 글

객체 지향적 설계 원칙 : SOLID  (0) 2018.12.14
디자인 패턴 (Design Pattern) 이란?  (0) 2018.09.17

 디자인 패턴 (Design Pattern) 이란?

 

- "초기에는 새로운 요구 사항을 빠르게 개발해 주었는데, 시간이 지날수록 간단한 요구 사항 조차도 제 때 개발이 안되는 상황"을 방지.

- 요구 사항의 적용에 있어 유연함(Flexibility)를 얻을 수 있음.

 

위와 같은 객체 지향적 소프트웨어 개발 방법론이다.

 

* 절차 지향 (Procedural Oriented) | 데이터를 조작하는 프로시저(혹은 함수)로 프로그램을 구성하는 기법.

장점 : 절차적으로 수행되는 코드에 의한 시간 단축.

 -> 하드웨어의 발전이 더딘 과거 개발 환경에서의 큰 이점으로 작용.

단점 : 수정 사항 반영 시 기존 개발된 절차에 영향을 받는 부분을 전반적으로 수정해야 하기 때문에 효율성 저하.

 -> 하드웨어 사양의 발전으로 인하여 절차적 수행에서의 시간 단축은 큰 의미가 없어짐.

 

* 객체 지향 (Object Oriented) | 데이터와 프로시저를 객체(Object)로 묶어 관리. 객체 구조에 의존된 기법.

장점 : 객체 구조에서 데이터와 프로시저의 독립성과 프로젝트의 유연성을 동시에 갖출 수 있음.

 -> 거대한 솔루션 급 소프트웨어의 추가 개발과 유지 보수, 커스터마이징까지를 생각한다면 비용을 고려했을때 절차 지향 기법보다 효율적.

 -> 대다수의 소프트웨어 업체의 퀄리티가 이러한 객체 지향적 설계에 대한 노력에 의해 평가됨.

단점 : 초기 설계 단계가 매우 중요한만큼 절차 지향보다 더 많은 시간과 노력이 소모됨.

 -> 초기 단계에서는 더딜 수 있으나 추후 안정성을 고려한다면 반드시 사용되어야 할 기법 중 하나.

 

디자인 패턴은 객체 지향적 기법을 적용한 소프트웨어에서의 효율적이라고 정형화된 어떠한 규칙을 나타낸다고 볼 수 있다.

 

디자인 패턴을 들어가기에 앞서, 객체 지향에 대해 더욱 알아보도록 하자.

 

 

출처: 최범균, 『개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴』, 인투북스(2014)

'IT > Design Pattern' 카테고리의 다른 글

객체 지향적 설계 원칙 : SOLID  (0) 2018.12.14
객체 지향(Object Oriented) 이란?  (0) 2018.09.18

+ Recent posts