설계 원칙
기본 원칙들
Encapsulation, Abstraction
Inheritance, Polymorphism
Association/Aggregation/Composition
객체 : 속성 (Attribute) + 메소드(Method)
현실 세계에 존재하는 개체를 속성뿐만 아니라 기능까지 포함시킨 것
객체지향 : 현실 세계의 개념(concepts or things)을 객체로 표현함
객체지향 기술은 객체와 객체 간의 통신을 통해 프로그램이 구현
객체지향프로그램 = 메시지 전송을 통해 상호작용하는 객체들의 모임
* 모듈화된 설계 - 모듈 : 객체 , 변수 : 속성, 함수 : 메소드
객체지향언어 4대 요소
객체, 메시지 전송, 클래스, 상속
객체 : 상태(state) + 행위(behavior) = 자신의 상태에 대한 연산의 집합
메시지 전송 :
객체 간 상호작용 장치
메시지 = 연산(실인수)
클래스 : 객체 생성 틀
멤버 변수 집합 + 멤버 메소드 집합 + 생성자 (+ 소멸자)
상속 : 클래스 재사용 장치
* 상태 -- 멤버 변수 집합
* 행위 -- 멤버 메소드 집합
구성요소
객체
인터페이스, 타입, Subtype
Class , Abstarct class
인터페이스
외부(다른 객체)에서 접근 가능한 멤버의 집합
호출가능한 메소드 집합(interface)
Type
인터페이스 이름
타입 = [값의 범위(Domain) + 허용되는 연산의 집합]
Subtype
Super type의 인터페이스를 포함하는 타입 (상속)
A subtype inherits its super type / A subtype IS-A super type
하나의 객체는 여러 타입을 가질 수 있다.
Class
데이터를 추상화하는 단위
객체의 구현implementation: 객체의 내부 상태를 표현하고 연산을 구현한다.
클래스는 객체의 구현이며, 동시에 타입이다. | 객체를 표현하는 추상 데이터 타입
Abstarct class
코드 중복 제거 - subclass들의 공통 구현을 정의
범주화(categorization)
인스턴스
실행 중인 임의의 프로세스
클래스의 현재 생성된 객체
* 인터페이스는 여러 인터페이스 타입 상속 가능
추상화
설계 단계
공통적으로 사용 가능한 큰 틀
공통 성질을 추출하여 슈퍼 클래스로 구성한다.
종류
기능 추상화, 자료 추상화, 제어 추상화
추상 클래스
어느정도 설계, 또는 개발된 클래스의 나머지 추상화된 부분을 상속받아서 구현
클래스들 사이에 공통적으로 사용하는 속성과 메소드를 추상 클래스로 선언
캡슐화
구현 단계
코드와 데이터를 숨김 ( 외부의 부적절한 접근으로부터 보호 )
구조적 설계에서 모듈화와 같은 의미로 객체를 정의할 때 연관된 속성(자료구조, 데이터)과 방법(함수, 기능 연산 등)을 한 테두리로 묶는 것
정보 은폐
캡슐화된 객체 내부에 속성이나 메소드의 기능이 외부에 영향을 받거나 주지 않도록 설계하는 방법
* 관점에 따라 똑같은 대상이라도 관심 사항에 따라 달라진다.
클래스 상속
코드 공유
class functionality 확장
인터페이스 상속 (Subtyping)
한 타입(subtype)이 다른 타입(super type)의 인터페이스를 포함(상속)
S is a subtype of T (S <: T) / S는 T의 서브타입
* subtype 객체는 super type 객체를 대치할 수 있다.
--> name(Number x){} <-- 호출 name(Integer)
다형성
상속받은 여러 개의 하위 객체들이 다른 형태의 특성을 갖는 객체로 이용될 수 있는 성질
타입 공유 : 객체들은 같은 타입 (interface) 다른 구현을 가질 수 있다.
동적 바인딩 : 인스턴스 메소드 바인딩은 참조변수의 실제 타입에 의해 결정
I_Animal iAnimal = new Dog();
선언타입 : I_Animal , 실제타입 : Dog
다형성 = 타입 공유 + 동적바인딩 : 같은 타입 객체들이 다른 행동을 할 수 있게 함.
--> 인터페이스에 의존 == 추상화에 의존
인터페이스
interface I_Animal
{
void eat();
void learn();
}
interface IDog extends I_Animal
{
void learn();
}
interface ICat extends I_Animal
{
void learn();
}
interface ICaw extends I_Animal
{
void learn();
}
Dog 구현 클래스
class DogImpl implements IDog
{
@Override
public void eat()
{
System.out.println("먹다");
}
@Override
public void learn()
{
System.out.println("개가 배우다.");
}
}
실행
public class Main {
public static void main(String[] args) throws Exception{
I_Animal m = new DogImpl();
m.learn();
}
}
개가 배우다.
다형성 프로그래밍 시 지켜야할 규칙들
- 기본) 상위 개념(공통 개념)은 인터페이스에서 정의 + 구현 클래스 각각의 유형
- 인터페이스 사용자는 구현 클래스를 알 필요 없다.
- subtype 객체는 super type 객체가 기대되는 곳에 항상 사용 가능
- 클래스 의존성은 가능한 더 추상적이어야 함
의존하는 타입의 범위가 넓어지므로 변경 가능성이 낮아진다.
Object Oriented Design Principles
객체지향 디자인 원칙 적용
1. Do NOT always try to solve the problem with design patterns and design principle
they are mostly for large enterprise project which has longer maintenance cycle.
Apply principles Reactively rather than Proactively
(패턴/원칙을 사전에 적용하지 말고, 문제가 발생하면 적용하라)
2. Professionals should always strive for
a highly cohesive & loosely coupled solution.
(응집도 높이고 결합도 낮춤)
객체 지향 설계 원칙, 설계 패턴 --> A system with resilient architecture (변경에 강한 구조)
SOLID
- Smells of Poor Design
변경하기 어려움 (Rigidity, Ripple effects : produces effects which spread and produce further effects\
변경이 여러 무관한 부분에 영향을 줌
시스템을 재사용 가능한 모듈로 재조직하기 어려움
Needless complexity (too clever code structure)
Needless repetition (코드가 중복됨)
Viscosity (점성도, 편집-컴파일-테스트, 즉 개발환경이 나쁨)
Opacity (originator의 의도가 난해하게 설명되어 있음)
- 종속성 관리가 중요 (Managing dependencies)
interfaces
polymorphism
Single Responsibility Principle | 하나의 클래스는 한 가지의 책임을 가진다. |
Open Closed Principle | 확장에 대해 열려있고(기능 추가), 변경에 대해 닫혀있어야 한다.(기존 코드 변경) |
Liskov Substitution Principle | subtype 은 supter type으로 대치할 수 있어야한다. |
Interface Segregation Principle | 하나의 큰 interface를 피하라 |
Dependency Inversion Principle | 변경 가능성이 높은 concrete class 의존하지 않아야 한다. Depends on Abstarcation |
1. DRY (Don't Repeat Yourself)
의미
코드를 중복 작성하지 말고 추상화 하라
추상화는 코드 중복을 피하기 위한 것이 아니라,
기능(functionality) 중복을 피하기 위한 것이다.
2. Encapsulate What Changes
의미
변경 가능성이 있는 구현 세부 사항은 캡슐화 하라 (숨겨라)
-> Easy to test and maintain proper encapsulated code (직접 접근 x)
3. Favor Composition over Inheritance (상속 보다 구성)
의미
코드 재사용은 클래스 상속 또는 구성(Composition)을 통해 이루어진다.
상속
Composition (& Delegation)
4. Programming for Interface not implementation
의미
variables, return types, parameter 에 인터페이스 타입 사용하기
- 선언타입에 상위타입 사용하기
ArrayList<Integer> numbers = getNumbers();
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
List<Integer> numbers = getNumbers();
5. Delegation principle
위임원칙 : 특정 행동에 대해 권한을 다른 사람에게 부여
위임자 / 수임자
수임자는 위임받은 일을 처리, 그 결과에 대해 책임짐.
의미
코드 재사용 / 행동의 변경 쉬워짐 / 코드 중복 방지
ex) if (obj1.equals(obj2))
해석 : 두 객채의 equality test 를 객체에게 위임
fireEvent() {
Event e = new Event();
for (Listener listener : listeners)
listener.handle(e); // 위임
}
위임
수임자는 위임자 context 내에서 수행
(note) this = current receiver (현재 호출된 객체)
*super 키워드
super 키워드는 부모 클래스로부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 변수입니다.
Forwarding : 전달 , a 객체가 직접 호출
Delegation : 위임 , 참조 변수를 통해 부모 클래스의 메소드를 자식 클래스에서 참조
6. Principle of Least Knowledge (PLK)
객체들이 Loosely coupled 되게 하라
class C
{
method( Object o )
{
- 클래스 자신의 메소드
- 파라미터 객체의 메소드
- 메소드 자신이 생성한 객체의 메소드
- 멤버 객체의 메소드
}
}
피해야 되는 것
Violating
public class Sample {
public void PLKtest(ClassA obj) {
ClassB another = obj.get(); //1
another.doSomething(); //2
}
}
객체로부터 다른 객체를 획득하고 //1
이의 메소드를 호출함 //2
⇨
이를 피하는 것이 PLK의 기본 생각
PLK
파라미터로 받은 객체를 (실제 타입) 실행
public class Sample {
public void PLKtest(ClassB obj) {
obj.doSomething();
}
}
ClassB obj 는 선언 타입이고
obj.doSomething(); 의 obj 는 객체의 실제 타입
: Loosely coupled
7. Single Responsibility Principle (SRP)
하나의 클래스는 오직 하나의 책임(or Functionality)을 가져야 함
: Highly cohesive class
if 클래스가 여러개의 책임을 가지면
두 functionality 사이에 결합이 도입될 가능성 존재
Less resilient (변경에 저항성이 약하다.)
해결책 => Separation of Responsibilities
☆ 객체에게 어떠한 속성을 주는 인터페이스를 사용할 때 주의해야 된다.
문제점)
Client가 Persistable 에게 dependent 하게 된다.
* Client와 Persistable과 관계가 없다.
해결책
PersistableEmployee는 Persistable을 구현
Employee를 상속 받음
PE가 변경되어도 Client 는 영향을 받지 않는다.
class PersistableEmployee implements Persistable extends Employee
Employee emp = new PersistableEmployee();
(client code segment)
Persistable의 변경이
∙ PersistableEmployee에 영향 주지만,
∙ Employee의 client에게는 영향을 주지 않음. (No dependency)
문제점 )
원인: Not a single responsibilities.
해결책 => Separation of Responsibilities
Geometric Rectangle class 를 생성해서 의존
독립적 이다. -> 다목적 클래스 만들면 안된다.
8. Open Closed Principle (OCP)
Software entities(module, classes(OOPL), functions(절차적언어) should be open for extension , but closed for modification
기능적 확장에 열려있고, 기존 코드 변경에 닫혀있다.
클래스는 소스 코드 변경없이 기능이 확장 될 수 있어한다.
- > GraphicEditor 는 소스 코드 변경없이 기능이 확장 (새로운 타입의 도형 추가) 될 수 있어야 한다.
클래스는 자신의 환경의 변경에 영향을 받지 않아야 한다.
* 환경 : 나(GE) 와 상호작용하는 모든 것(class)
☆ Abstraction is the key to OCP - 닫힘은 추상화에 기반 한다:
추상화 : 공통적인 요소 (ex 동물은 사람과 개의 추상화 )
1. Bertrand Myer's OCP - 클래스 상속을 사용한다. - 구현을 고정
2. Polymorphic OCP - 인터페이스 상속을 사용한다. - interface 고정
Fixed abstraction & Unbounded group of subclasses
without(w/o) affecting clients
(추상화가 고정, 무한한 그룹)
Client는 Abstraction 에 의존
☆ 변경 가능성있는 부분을 (분리하고) 추상화
9. Dependency Inversion Principle (DIP) / 의존성 역전 원칙
concretions (concrete class) 에 의존하는 것이 아니라 Abstractions(interface) 에 의존
* OCP 은 목표(Goal)이며, DIP 은 Primary Mechanism 이다.
의미
- Every dependency should target an interface or an abstract class.
- Anything concrete is volatile (변경 가능성). No dependency should target a concrete class
단) 변경 가능성이 없으면 한 클래스에 의존해도 괜찮다.
객체 생성시 DIP를 위반하지 않고 객체를 생성하려면 Abstract factory
"Use" 관계를 이용한 설계
USE dependency
- 상위 모듈은 하위모듈 사용(호출) / 역은 불가
- layered architecture
문제점)
Manager 가 Worker class 를 의존하고 있을때
새로운 worker 가 추가되면 Manager 는 기존 코드 변경을 해야된다.
추상화
concrete class 의 의존이 abstract class 에 의존 : 의존성 역전
Dependency Inversion(DI) 을 이용한 설계
- 하위 모듈의 abstraction 도입
interface Worker {
public void work();
}
- Manager : 기존 코드
class Manager
{
public void manage(Worker worker) {
worker.work();
}
}
- 인터페이스(interface)를 구현(implements)
// ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ//
class NomalWorker implements Worker
{
public void work() {
...works...
}
}
class SuperWorker implements Worker
{
public void work() {
...works much more...
}
}
10. Liskov Substitution Principle(LSP)
Subclasses (Subtype) must be substitutable for their super classes (Type)
- 서브클래스는 슈퍼클래스를 대치할 수 있어야한다.
★ 서브클래스는 슈퍼클래스의 행동을 변경하지 않고, 단지 확장되어야 한다.
(상속한 것을 재정의 x , 새로운 멤버 추가(서브클래스 새로운 메소드 추가) o)
[마틴]
슈퍼클래스 참조자를 사용하는 메소드(client)는 서브클래스 객체를 사용할 수 있어야 한다.
SuperClass obj = new SubClass();
aMethod(SuperClass obj) and aMethod(new SupClass())
단★, 슈퍼클래스의 client는
- 서브클래스를 사용하기 위해 특별한 일을 하지 않아야한다. (타입채킹, 다운캐스팅)
- (특히, instanceof, downcasting`
- 서브클래스에 대해 (존재)전혀 알지 못해야 한다.
- 상속한 것을 재정의 X
[Liskov]
모든 타입 S (subtype) 객체 o1에 대해, 다음을 만족하는 타입 T (SuperType) 객체 o2가 존재한다면,
S는 T의 subtype (S <: T) 이.
=> T로 정의된 모든 프로그램 P에 대해,
o2를 o1(서브타입)으로 대치할 경우 P의 행동(behavior)이 변하지 않는다(unchanged).
Behavior Subtyping (대치)
슈퍼타입의 행동 - 언어가 지원을 X -> 프로그래머가 해당 행동을 지켜야함.
대치 - 언어가 지원
Square의 표현
- Square 은 직사각형의 특별한 경우이다.
- Shape 는 width, height 길이가 같을 수도 있고 다를 수도 있다.
경우 1
인스턴스 a 는 (서브클래스) 직사각형인지 정사각형인지 알지 못해야 한다.
Square에 대해 상속받은 메소드를 재정의 하였지만
만약 직사각형 멤버였다면...
경우 2
- Shape 추상화 클래스 도입
- 각각에 맞게 재정의
- Shape의 width height 의 제약이 없다. -> Shape의 행동 변경하지 않는 것이다.
다른 예)
- 자원봉사근로자는 급료 지불, 프린팅 시의 대상자가 아니다.
Employee (Super Class)의 사용자는 특별한 일을해야 한다.-> LSP 위반 / OCP 위반 (기본 원칙)
예외처리)
for (Employee e : employees)
{
try {
totalPay += e.calcPay();
} catch (UnpayableEmployeeException e) {}
}
타입식별)
for (Employee e : employees)
{
if (!(e instanceof VolunteerEmployee))
totalPay += e.calcPay();
}
※ VolunteerEmployee is NOT a Employee : 상속하지 않아야 한다.
[Meyer] Design by Contract
슈퍼클래스(메소드)는 불변 : 객체(의 상태)를 제한하기 위해 사용
Class Invariant (참고)
∙ Invariant
(수학) 연산/변환 후 변하지 않는 (수학적) 객체의 의 성질(특성 properety)
(컴퓨터) 특정 실행 단계 동안 항상 ‘참’이 유지되는 논리적 주장(변수의 사이에 성립하고 있는 관계의 표현)
∙ Class invariant
- 객체(의 상태)를 제한하기 위해 사용됨
- 메소드는 invariant를 유지해야(preserve) 함
- 객체 생성 시 형성되며, public method 호출 간 항상 유지되어야(maintain) 함
∙ class invariant의 상속
- 모든 parent의 class invariant는 상속된다. 즉 클래스(child)에게 적용된다.
- Misbehaving descendant
재정의 시 parents’ invariants를 위반하는 경우가 발생할 수 있다.
11. Interface Segregation Principle (ISP)
의미
- 각 client 에 특화된 작은 interface 를 만들기
- 인터페이스는 오직 사용자가 필요로 하는 메소드들만 제공하게 하기
'소프트웨어공학 > 디자인 패턴' 카테고리의 다른 글
Facade 패턴 (퍼사드 패턴) - 구조, 통합 인터페이스 제공 | Design Pattern 디자인 패턴 / (0) | 2021.10.12 |
---|---|
Adapter 어댑터 패턴 - 구조, client 가 요구하는 인터페이스와 제공된 클래스의 인터페이스가 일치하지 않을 때 | Design Pattern 디자인 패턴 (0) | 2021.10.10 |
싱글톤 패턴 코드 (Singleton pattern java code) | Design Pattern 디자인 패턴 / (0) | 2021.10.08 |
Singleton 패턴 클래스의 인스턴스가 하나만 있는 것 | Design Pattern 디자인 패턴 (0) | 2021.10.05 |
[Design Pattern] 디자인 패턴 / UML Base .java (0) | 2021.09.11 |
댓글