본문 바로가기
정보관리기술사&컴퓨터응용시스템기술사/SW공학과 프로젝트관리

[객체지향 설계 5원칙] 유지보수와 확장에 용이한 시스템을 위한 원칙

by 별프로 2021. 1. 6.
반응형

I. 유지보수와 확장에 용이한 시스템을 위한, 객체지향 설계 5원칙의 개요

가.  객체지향(Object-Oriented) 설계원칙의 개념

- 코드 작성시 유지보수, 유연성, 확장성을 용이하게 하기 위해 SRP, OCP, LSP, ISP, DIP로 구성된 설계 원칙

시스템에 새로운 요구사항이나 변경이 있을 때 영향도를 최소화하는 설계

나. 객체지향 설계원칙의 필요성

- 잘못 관리된 의존성 때문에 Design Smell 발생하여 유지보수/확장의 어려움  ※ Spaghetti Code

의존성 관리에 대한 가이드 필요

[참고] Design Smell

Rigidity (경직성)

특정 모듈을 수정하면 다른 모듈을 계속 수정해야 함

Fragility (취약성)

특정 모듈의 수정이 다른 모듈에 문제를 일으킴

Immobility (부동성)

Component를 만들기 어려움, 연관성 없는 모듈과 강하게 연결되어 있음

Viscosity (정착성)

Design에 맞춰 code를 집어 넣지 않고 hooking으로 추가가 더 용이함

Needless Complexity

불필요한 복잡성

Needless Repetition

불필요한 반복 ← Cut & Paste의 과도한 사용

Opacity (불투명성)

제작자의 의도를 알기 어려움

 

[참고] Object-Oriented Design Principles

- 구현이 아닌 인터페이스에 맞춰서 프로그램해라. Program to interfaces, not implementations

- 클래스 상속보다는 구성을 활용해라. Favor object composition over class inheritance

- Application에서 달라지는 부분을 찾아 내어 분리해라. Encapsulation what varies

- 상호작용을 하는 객체 간에는 가능하면 느슨하게 결합하는 design을 사용해라.
Strive for loosely couple design between that interact

- SOLID principles by R.C. Martin

 

II. 객체지향 설계의 5원칙의 주요 내용 및 원칙별 상세내용

가. 객체지향 설계의 5원칙의 주요 내용

원칙

설명

역할

단일 책임 원칙
(SRP)

- Single Response Principle

- 객체는 하나의 책임만을 맡아야 한다.

- 하나의 클래스는 단일 역할 및 책임만 수행

변화 적응도 및 유지보수성 향상

개방 폐쇄 원칙
(OCP)

- Open Closed Principle

- 확장에는 Open, 수정에는 Close 되어야 한다.

기존 코드 변화없이 확장으로 코드 변경 지향

리스코프 치환 원칙
(LSP)

- Liskov Substitution Principle

- 하위 클래스 및 타입들은 상위 타입들이 사용되는 곳에 대체 가능

- 자식들은 부모 타입들이 사용되는 곳에 대체되어 사용될 수 있어야 함

클래스 생성 목적에 맞게 설계하기 때문에 상/하위 클래스의 호환성 향상

인터페이스 분리 원칙
(ISP)

- Interface Segregation Principle

- 클라이언트는 자신이 사용하지 않는 메소드에 의존 관계를 맺으면 안된다

- 클래스가 다른 클래스에 종속될 때, 최소한의 인터페이스만을 사용

클래스가 사용하지 않는 불필요한 기능의 구현을 방지

의존성 역전 원칙
(DIP)

- Dependency Inversion Principle

- 추상화 된 것에 의존하게 만들고, 구체 클래스에 의존하지 않도록 한다.

- 높은 레벨의 모듈은 낮은 레벨의 모듈을 의존하지 않는다는 원칙

- 낮은 레벨의 구현 클래스에 의존성을 제거하여 낮은 결함도 유지

추상화된 클래스에 의존하기 때문에 확장성이 높아짐

 

나. 단일책임의 원칙 (SRP : Single Responsibility Principle)

구분

설명

정의

- 코드를 모듈화 방법을 결정하는 핵심원칙 (높은 응집도, 낮은 결합도)

- 시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나 만의 책임만을 수행해야 한다는 설계 원칙

목적

- 변경 해야할 부분을 하나의 Class로 처리하여 유지보수를 용이하게 함
변화에 유연성 확보

1) 낮은 결합도 추구: 모듈간의 연관 관계를 낮춰 변경 시 타 모듈에 영향을 주지 않음

2) 높은 응집도 추구: 연관된 기능들을 모아 구현하여 유지보수를 용이하게 함

※ SRP 위반 시, 변경시 해당 부분을 찾기 어렵고, 누락하고 작업하기 쉬움

개념도

        다수책임 수행                                  단일책임 수행                

고려

사항

- 책임을 억지로 나누지 말것 (책임을 수정의 단위로 해석하면 용이)
모든 class SRP를 적용하면 Needless Complexity를 유발할 수 있음

산탄총 수술 (Shotgun Surgery) = 기능 산재

- 변경을 할 때 마다 많은 클래스를 조금씩 수정해야 하는 경우, 변경 시 해당 부분을 찾기 어렵고 누락하고 작업하기 쉬움.

- Move Method, Move Field를 사용하여 변경해야 할 부분을 하나의 Class로 처리 필요

예시

Shopping 클래스에서 Member에 대한 책임을 분리하여 Class 생성 (Move Class)

 

다. 개방 폐쇄 원칙 (OCP : Open Closed Principle)

구분

설명

정의

- 확장에 용이한 설계 원칙 (추상화가 핵심임)

- 소프트웨어 Entity(Classes, Modules, Functions)는 확장에는 열려있고, 수정에는 닫혀 있어야 한다는 설계 원칙

목적

- 기존의 코드 변경 없이 새로운 코드를 통해 모듈 확장 (모듈은 결코 변경되지 않음)
소프트웨어 유지보수에 용이

※ OCP 위반 시, 하나의 변경이 모든 변경의 파급효과를 가져옴 (Rigidity, Fragile, Immobility)

개념도

- 기존의 모듈을 변경하지 않고(Closed), Client Code를 추가(Open)하여 설계

- 모듈 : 변경되지 않는 핵심 요소

- 클라이언트 코드 : 모듈에서 상속받은 기능을 가지고 오버라이딩함

고려

사항

- 추상화(Abstraction)가 핵심임.

. 변화/수정/확장되는 것은 concrete class로 만들고, 변하지 않고 다른 개발자가 사용해도 되는 classabstract class로 정의

- 상속을 통해 유연하게 모듈 확장 가능

. 오버라이딩 상속을 의미하지만, 크게 유연성 확보 차원의 원리

  . 기능의 상속이 아닌 설계의 유연성을 강조

예시

(상속)

- 계산기의 변하지 않는 부분만 Calculator로 추상화 Specific한 계산기는 상속받아서 구현.
새로운 유형의 계산기 도입 시 Calculator 클래스 수정없이 Calculator 클래스를 상속받아 새로운 클래스 생성 가능

예시

(Framework)

- framework : Application에 독립적인 부분

- customization : Application에 의존적인 부분은 비즈니스 로직 및 데이터를 반영

 

라. 리드코프 치환의 원칙(LSP : Liskov Substitution Principle)

구분

설명

정의

- 상속을 사용할지 Composition을 사용할지를 결정하는 원칙

- 자식 타입(Subtype)은 상위 타입들이 사용되는 곳에 대체 될 수 있어야 한다는 원칙

- 자식 타입은 부모 타입으로 교체할 수 있도록 설계한다는 원칙

목적

- 다형성(polymorphism)을 안정적으로 사용가능 하도록 하는 설계원칙

- 인터페이스만 알면 구현체를 몰라도 사용 가능함

구현체의 변경 및 확장이 클라이언트와 독립적으로 발생하여, 유지보수/확장 용이

  ※ 위배 시, 인터페이스의 기능을 수행하는 클라이언트가 호환되지 않는 구현체를

   사용하게 되는 문제 또는 오류 발생

개념도

고려

사항

- 상속보다는 Subtyping의 개념임. 대부분의 프로그래밍 언어가 상속 시 Subtyping도 동시에 되기 때문에 혼동이 됨.

- 상속으로 설계 시, LSV 위반이 발생하는 경우는 Composition을 사용하여 Subtyping하는 것이 바람직함.

예시

Storage 인터페이스를 구현한 하위클래스인 FileStorage DBStorage 클래스는 Client write() 인자인 Storage를 대체 가능해야 함

→ Client는 어떤 Storage 사용하는지에 관계없이 Storage Save를 사용하여 개발. FileStorage/DBStorage의 변경, 다른 유형의 Storage 생성에 영향받지 않음

 

마. 인터페이스 분리의 원칙(ISP : Interface Segregation Principle)

구분

설명

정의

- 클라이언트별로 관련된 인터페이스만 제공해야 한다는 원칙

- 하나의 일반적인 인터페이스보다는 구체적인 여러 개의 인터페이스로 구현해야한다는 원칙

- 클라이언트는 자신이 사용하지 않는 메소드에 의존 관계를 맺으면 안됨

목적

Client가 사용하지 않는 Method에 대한 의존관계를 없애, 유지보수가 용이

구현 클래스에서 불필요한 Interface에 대한 기능 구현 방지

위반 시, 불필요한 coupling이 발행하여, 사용하지 않는 method 추가/수정 시, 기능 구현 및 컴파일/테스트해야 하는 등의 문제 발생

※ Façade Design Pattern : 인터페이스의 단일 책임을 강조

하나의 인터페이스에 해당 인터페이스의 목적에 부합되지 않는 기능을 선언하지 않기 때문에 구현 클래스에서 불필요한 기능의 구현 방지

개념도

-각 클라이언트에 필요한 기능별로 Interface를 분리

고려

사항

- 모든 인터페이스를 분리하는 것이 아니라, 공통적인 인터페이스는 추상화하여 재사용을 극대화 → Façade Pattern

예시

네트워크 서비스 제공 시, 클라이언트별로 사용하는 Protocol에 대한 인터페이스를 분리하여 클라이언트별로 제공

 

바. 의존성 뒤집기 원칙 (DIP : Dependancy Inversion Principle)

구분

설명

정의

- 상위 계층이 하위 계층에 의존하는 전통적인 의존관계를 반전시킴으로써, 상위 계층이 하위 계층의 구현으로부터 독립되게 SW 모듈을 분리하는 원칙

- 상위 수준 모듈은 하위 수준 모듈에 의존성이 없어야 하고, 서로 추상화에 의존해야 한다는 원칙

- 추상화된 것에 의존하게 만들고, 구체 클래스에 의존하도록 만들지 않도록 함.
자주 변경되는 구체 클래스에 의존하지 마라

목적

- 구체클래스에 의존성을 제거하여 낮은 결합도 유지

- 추상화된 클래스에 의존하여 확장성이 높아져 유지보수성 향상

개념도

상위 클래스 A가 하위 클래스 B에 의존하지 말고, 추상화 클래서 A*에 의존하게 함.

고려

사항

- 모든 클래스에 인터페이스를 생성하면 클래스가 엄청나게 증가하고 복잡해지므로 필요한 것만 생성

- OCP LSP를 코드에 적용하면, DIP 또한 준수하게 됨

예시

- Copy Read Keyboard, Write Printer에 의존하지 않고 interface에 의존하도록 하게 함으로써, 새로운 입력장치 Scanner, 출력장치 FlashDisk를 독립적으로 추가 가능

 

※ MVC 패턴이 DIP의 대표적인 예임

 

[참고] 객체지향 설계 원칙 활용

* 객체지향 설계 5대 원칙 활용

   (1) 개발 위한 원칙이 아닌 설계 원칙

   (2) 클래스 간 낮은 의존성, 파생 클래스 활용도 극대화

   (3) 부모 클래스를 상속받아 최대한 활용하도록 설계

   (4) 객체지향 특징, 장단점 파악, 적절히 Trade-off

 

* 객체지향설계 적용 시 고려사항

항목

내용

프로젝트 착수 시

- 재사용에 대한 규모 산정 및 재사용 체계 구축에 대한 수립이 필요

- 재사용성 활용도 측면 고려 필요

프로젝트 수행 시

- 분석/설계 시에 클래스의 책임과 역할 분석을 위한 CRC (Candidate, Responsibility, Collaboration)카드 작성

- 재사용성을 극대화 하기 위한 Design Pattern 활용

유지보수 수행 시

- 재사용 컴포넌트 관리와 프로세스(기능)과 연계 관리가 필요

- 컴포넌트의 조립으로 결합 컴포넌트 생성

 

반응형

댓글