OOP 5대원칙
태그 :
- 개념
- 객체지향 소프트웨어 설계의 근본 원칙, 디자인 패턴의 근본 원칙 객체지향 소프트웨어 설계 도구들의 근본이자 목적이 되는 원칙
1. 보다나은 프로그래밍을 위하여, 객체지향 설계의 5대 원칙의 개요
가. 객체지향 설계의 5대 원칙의 개념
객체지향 소프트웨어 설계의 근본 원칙, 디자인 패턴의 근본 원칙
객체지향 소프트웨어 설계 도구들의 근본이자 목적이 되는 원칙
나. 객체지향 설계의 5대 원칙의 중요성
재사용성, 유지보수성의 향상을 위해(높은 응집도, 낮은 결합도)
설계 원칙을 기반으로 디자인 패턴 또는 유용한 개발론들이 등장하고 있음
2. 객체지향 설계의 5대 원칙 (SOLID)
원칙 |
설명 |
사례 |
단일 책임의 원칙 (SRP) |
- 객체는 하나의 책임만을 맡아야 함 - DB 정규화와 비슷함 (성능저하라는 부작용이 없음) - 핵심: 변화 - 목적: 변화에의 유연성 확보 (낮은 결합도, 높은 응집도 추구) |
- 데이터 매퍼 패턴(DAO) |
개방폐쇄 원칙 (OCP) |
- 모듈은 확장에는 열려있어야 하고 변경에는 닫혀있어야 함 - 방법: 변화는(확장되는) 것과 변하지 않는 것을 엄격히 구분한 후 이 두 모듈이 만나는 지점에 인터페이스를 정의 |
- 상속과 어댑터 클래스를 통한 클라이언트 클래스 접속 - 컴파일러, POSIX 표준 |
리스코프 치환원칙 (LSP) |
- 기반 클래스는 파생 클래스로 대체 가능해야 함 - 즉, 인터페이스만 알면 구현체를 몰라도 사용 가능해야 함 |
- |
인터페이스 분리의 법칙 (ISP) |
- 하나의 일반적인 인터페이스보다는 구체적인 여러 개의 인터페이스가 나음 - 핵심: 변화 - 목적: 변화에의 유연성 확보 - 효과: 인터페이스의 통합과 분리 |
- 파일입력은 InputInterface, 파일출력은 OutputInterface - 공유 리파지토리 패턴 |
의존관계 역전의 원칙 (DIP) |
- 클라이언트는 구체 클래스가 아닌 인터페이스나 추상 클래스에 의존해야 함 - Bridge 패턴처럼 인터페이스/추상 클래스 간에만 서로 의존관계를 가지며 참조 - 모든 클래스에 인터페이스를 생성하면 클래스가 엄청나게 증가하고 복잡해지므로 필요한 것만 생성 |
- 이벤트 드리븐, 콜백, JMS, 통신 프로그래밍 모델 |
2. 사례를 통한 객체지향설계의 5대 원칙
가. SRP(Single Responsibility Principle ) : 단일책임원칙
1) 원칙 : 하나의 클래스는 하나의 책임만 가져야 한다.
2) 상세설명 :
-하나의 클래스에 책임이 너무 많아지면 Collaboration(협력) 관계를 통해 클래스를 세분화해야 한다. 즉 책임을 나누는 것이다. 여기서 책임을 나누는 기준, 범위 등에 대한 정답은 없다. 다만, 유지보수 및 관리의 차원을 면밀히 고려하여 나눠야 한다.
-시스템의 모든 객체는 하나의 책임만을 가지며,객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다.
Example
나. ISP(The Interface ISegregation Principle ) : 인터페이스 격리 원칙
1) 원칙 :
-파생 클래스 입장에서 사용할 때 100% 구현할 수 있는 인터페이스만 사용해야 한다.
-인터페이스를 함수를 통해 사용할 때 자신의 목적에 맞게 사용해야 한다.
2) 상세설명 :
-최소한의 인터페이스만 구현한다. → 재사용성 극대화
: 예를 들어 은행 관련 입금, 송금, 출금 모듈을 만들었을 경우 상황에 따라 각 모듈을 골라서 사용할 수 있어야 한다. 만약 여러 기능을 하나로 묶어두면 재사용이 어렵게 된다.
-다중 인터페이스 상속:
두 개 이상의 인터페이스가 필요한 경우 다중 인터페이스 상속으로 구현하는 것이 좋다.
-인터페이스가 인터페이스를 상속:
인터페이스가 인터페이스를 확장하는 경우는 기존 기능에 대한 확장이 요구될 때 채택할 수 있다.
다. DIP ( The Dependency Inversion Principle ) : 의존 관계 역전 원칙
1) 원칙 :
-추상 클래스는 파생 클래스를 참조해서는 안되며, 파생 클래스나 추상 클래스는 오직 추상 클래스만을 참조해야한다.
2) 상세설명 :
-DIP 원칙은 클래스 군들 간에 설계원칙에 대한 지침이다.
-DIP 원칙은 클래스 군들 간의 협업을 어떻게 하느냐에 중점을 둔 원칙이다.
-파생 클래스가 의존할 때는 추상 클래스에 의존
-추상 클래스가 의존할 때는 추상 클래스에 의존
-결국, 의존의 대상은 추상클래스나 인터페이스가 되야 한다.
-UML Example
라. LSP ( The Liskov Substitution Principle ) : 리스코프 치환 원칙
1) 원칙 :
-자식 타입들은 부모 타입들이 사용되는 곳에 대체될 수 있어야 한다.
2) 상세설명 :
-LSP 원칙은 OCP 원칙에 따라 디자인된 클래스들을 활용하는 단계에서 요구되는 원칙이다. 추상적인 클래스를 통해서 추상적인 클래스 이면에 숨어있는 구체적인 클래스를 제어하는 데 관심이 있다. ( 제어의 융통성 )
-LSP 원칙은 함수 의존적이다. :
LSP 원칙은 함수에서 사용될 때에 함수의 인자는 추상클래스나 인터페이스를 받아서 사용한다. 치환하는 대상은 구체적인 클래스에서 기반 클래스로 치환하는 것을 의미한다.
-LSP는 잘 디자인된 상속에 관한 내용입니다. 부모 클래스를 상속할 때, 부모 클래스가 사용되는 곳은 아무 문제없이 자식 클래스도 사용할 수 있어야 합니다. 그렇지 않으면, 상속을 잘못 사용하고 있는 것입니다.
3) 효과 :
-OCP 원칙에 따라 디자인된 클래스를 사용할 때는 치환된 기반 클래스를 활용함으로써 모든 파생 클래스가 자유롭게 돌아다닐 수 있는 공간이 만들어진다.
마. OCP ( The Open-Closed Principle ) : 개방-폐쇄의 원칙
1) 원칙 :
-확장에 열려 있고, 변경에 닫혀 있어야 한다
2) 상세설명 :
-라이브러리 개발자에게 변경이 발생하면 라이브러리 사용자에게도 변경이 발생한다.
☞ 확장 및 유지보수의 어려움 ( 유연성 ↓ )
-확장, 수정이 가해지는 클래스의 영향이 밖으로 흘러나가지 않도록 중간에 완충장치를 두어야 한다. 라이브러리 개발자에게 일어난 변화가 사용자에게 영향을 주지 않토록 어떤 완충장치를 마련한다.
☞ 유연성
-OPEN : 클래스 수직관계(Is-a)에서는 열려있어야 한다. 기반 클래스에서 파생 클래스로 확장
-CLOSE : 클래스 수평관계(has-a)에서는 유연해야 한다. 즉 영향을 받지 않아야 한다.
3) 어떻게? : 추상화 도입 → 추상 클래스, 인터페이스
-변하는 것과 변하지 않아야 하는 것을 엄격하게 구분해야한다. 즉, 변하는 것은 변하기 쉽게, 변하지 않아야 하는 것은 영향을 받지 않게 디자인하는 것이다.
-Step 1 클래스 사이에 존재하는 공통적인 속성을 추출
-Step 2 추출된 속성은 하나의 인터페이스 또는 추상 클래스로 디자인하기
-Step 3 이렇게 디자인된 인터페이스 또는 추상 클래스를 상속하기
6) 효과
-유지보수에 강하고 재사용성을 극대화할 수 있다. → 유연성 ↑
-도둑잡기 → 컴파일 단계 에러 체크 시스템을 사용할 수 있다.
-UML Example
가. 객체지향(Object Oriented) 정의
실 세계의 개체(Entity)를 속성(Attribute)과 메소드(Method)가 결합된 형태의 객체(Object)로 표현하고 객체간의 메시지를 주고 받는 형태로 시스템을 구성하는 개념
나. 객체지향을 위한 class design 5대 원칙의 개념
객체지향방법론이 지향하는 재사용과 유지보수 극대화를 달성하기 위해 클래스 단위에서의 설계에 대한 원칙을 정의
다. Class Design을 위한 5대 원칙
4. 단일 책임의 원칙(SRP: The Single Responsibility Principle)
가. SRP 정의
-클래스에는 한 가지 종류의 책임만을 두어야 한다는 원칙
나. SRP 사례
(변경 전)
-한 개의 클래스(Shopping)에 회원관리와 상품관리 기능이 동시에 있음
-회원관리, 상품관리 한쪽에 변경이 발생하면 Shopping 클래스가 수정되어야 하고, 관련된 모든 프로그램이 동시에 수정되어야 함
(변경에 취약)
(변경 후)
-Member와 Shopping 클래스로 분리
다. SRP 설계 시 주의사항
-SRP는 대상이 함수나 메소드가 아닌 객체라는 점에 주목
- 객체는 둘 이상의 책임을 갖지 않는 형태를 가져야 함 - 즉, 두 개 이상의 메소드나 프로퍼티를 가졌을 때 책임이 그 수만큼 늘어나게 된다면 과감하게 분리 (Extract Class, Extract Method) |
-SRP는 하나의 객체가 두 개의 책임을 가지는 것 만큼이나 두 개의 객체가 하나의 책임을 나누는 것에 주의를 기울여야 함
- 단일 요구사항의 변경으로 둘 이상의 객체가 변경을 요하는 상황에 처 한다면 책임이 나뉘었다고 판단하고, 하나의 객체가 온전히 책임을 다 가질 수 있도록 해 주어야 함 (Move Method, Move Field) |
5. 인터페이스 분리의 원칙(ISP: Interface Segregation Principle)
가. ISP 정의
클라이언트는 자신이 사용하지 않는 메소드와 의존 관계를 갖지 않도록 해야 함
ISP를 ‘하나의 일반적인 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다’라고도 정의할 수 있음
만약 어떤 클래스를 이용하는 클라이언트가 여러 개 있고, 이들이 해당 클래스의 특정 부분집합만을 이용한다면 이들을 따로 인터페이스로 빼내어 클라이언트가 기대하는 메시지만을 전달할 수 있도록 하는 것임
나. ISP 사례
(변경 전)
-IManager 인터페이스 변경 시, 클래스 A, B, C 모두 변경되어야 함
(변경 후)
6. ISP vs SRP 접근법의 차이
ISP 접근법 |
SRP 접근법 |
- 하나의 인터페이스가 여러 개의 역할로 구성된 경우 - 아래 이체는 Façade 패턴이 됨 |
- 하나의 역할은 여러 인터페이스로 분해되기도 함 |
사례> ”이체” (1) 트랜잭션 시작 (2) 상대 계좌 존재 여부 확인 (3) 고객 계좌에서 금액 출금 (4) 상대 계좌에 입금 (5) 트랜잭션 종료 |
사례> java.io.PrintWriter 클래스 - printXX()류의 인터페이스로 역할을 세련되게 수행 |
-일반적으로 인터페이스와 역할은 1:1 관계를 가짐 즉, SRP를 접근할 때와 ISP를 접근할 때의 접근법은 차이를 두어야 하며, 비즈니스의 목적과 용도에 맞게 접근할 필요가 있음
객체지향 주요특징
SWOT |
상위개념 |
소프트웨어 개발 방법론 |
핵심키워드 |
현실세계의 반영,객체,클래스 캡추다정상(캡슐화, 추상화, 다형성, 정보은닉, 상속성) |
|
비교개념 |
정보공학방법론, CBD |
|
출제된 회차 |
|
7. 현실세계의 반영, 객체지향의 개요
가. 객체지향의 개념
실제 세계는 사물(객체)로 이루어져 있으며, 발생하는 모든 사건들은 사물간의 상호작용이다. 이 개념을 컴퓨터로 그대로 옮겨놓아 적용한 개념.
나. 객체지향의 등장배경
등장배경 |
내용 |
S/W 위기 극복 |
- 낮은 생산성, 납기 지연 극복 |
S/W복잡도 증가 |
- 기술발달, 다양성 증대로 시스템 규모 증가 |
사용자 요구증대 |
- 보다 많은 기능, 단순성, 사용용이성에 대한 요구 증대 |
8. 객체지향의 구성요소 및 주요특징
가. 객체지향의 구성요소
구분 |
내용 |
객체(Object) |
- 속성(데이터)과 행위(함수 또는 프로시져)를 갖는 사물 또는 개념 |
클래스(Class) |
- 객체들을 같은 속성과 행위를 가진것으로 분류해 놓은 틀 Ex) 붕어빵 틀, 사람 |
인스턴스(Instance) |
- 클래스에 의해서 생성되는 각각을 지칭. 즉, 실제 메모리상 에 할당된 객체 Ex) 붕어빵, 홍길동 |
메서드(Method) |
- 클래스로부터 생성된 객체를 사용하는 방법 |
메시지(Message) |
-Sender와 Receiver객체들간의 상호작용의 수단으로 다른 객체에 특정 작업을 요청하는 신호 -수신객체이름, 오퍼레이션 이름, 매개변수로 구성 |
나. 객체지향의 주요특징 (캡추다정상)
특징 |
상세내용 |
||||||||||||||||
캡슐화 Encapsulation |
-속성(데이터)과 메소드(연산)을 하나로 묶어서 객체로 구성 |
||||||||||||||||
개념도 |
|||||||||||||||||
특징 |
-접근제어 public : 외부 접근 가능 private : 외부 접근 불가 |
||||||||||||||||
장점 |
-Readability 향상 : 유지보수 용이 -재사용성이 높은 S/W 개발 가능 -정보은닉으로 내부자료 일관성 유지 -객체간 인터페이스를 이용, 종속성 최소화 |
||||||||||||||||
예제 코드 |
class Encapsule { private ArrayList widths = new ArrayList(); public ArrayList getWidths() { return widths } } -private 선언한 widths 는 외부 접근 불가, getWidths 메소드를 통한 접근 |
||||||||||||||||
추상화 Abstraction |
-공통 성질을 추출하여 수퍼클래스로 구성
|
||||||||||||||||
개념도 |
|||||||||||||||||
특징 |
-객체지향 언어에서는 클래스를 이용함으로써 데이터와 프로세스를 함께 추상화의 구조에 넣어 보다 완벽한 추상화 실현 |
||||||||||||||||
장점 |
-객체 중심의 안정된 모델 구축 -현실 세계를 자연스럽게 표현 -분석의 초점이 명확 |
||||||||||||||||
예제 코드 |
abstract class GraphicObject { int x, y; void moveTo(int newX, int newY) { … } abstract void draw(); } class Circle extends GraphicObject { void draw() { … } } class Rectangle extends GraphicObject { void draw() { … } } -공통성질(draw)을 추출하여 추상화 |
||||||||||||||||
다형성 Polymorphism |
-동일한 이름의 여러 오퍼레이션(메소드)을 다른 사양으로 정의 가능
|
||||||||||||||||
종류 |
Over-loading |
-메소드명 동일, argument or return type 이 다른 경우 |
|||||||||||||||
Over-riding |
-부모클래스 메소드를 자식클래스에서 재정의. argument, return type 동일 |
||||||||||||||||
예제 코드 |
class Parent { void parentMethod() {} } class Child extends Parent { void parentMethod() {} // Over-riding void parentMethod(int i) {} // Over-loading } |
||||||||||||||||
정보은닉 Information Hiding |
-캡슐화된 항목을 다른 객체(Object)로부터 숨김 -메시지 전달에 의해 다른 클래스 내의 메소드가 호출됨 |
||||||||||||||||
예제 코드 |
class InformationHiding { private List widths = new ArrayList(); public List getWidths() { return widths } } 외부에서는 widths 의 타입을 알 수 없음 |
||||||||||||||||
상속성 Inheritance |
-부모 클래스의 속성과 메소드를 상속받아 사용 |
||||||||||||||||
종류 |
단일 |
-부모와 자식 클래스 간의 관계가 수퍼클래스와 서브클래스로 유지 |
|||||||||||||||
다중 |
-하나의 클래스가 하나 이상의 클래스로부터 상속 받음 |
||||||||||||||||
반복 |
-같은 조부모 클래스로부터 상속 받은 두 부모 클래스로부터 상속 받는 것 |
||||||||||||||||
예제 코드 |
class Animal { public void move() { … } } class Dog extends Animal { public void bark() { … } } -Dog는 Animal 을 상속 받았기 때문에 move 와 bark 메소드 호출 가능 |