본문 바로가기
소프트웨어공학/디자인 패턴

[Design Pattern] 디자인 패턴 / 설계 원칙 .java

by javapp 자바앱 2021. 10. 2.
728x90

 

 설계 원칙

 

본 원칙들

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) / ST의 서브타입

  * 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

 

 

 

 

객체지향 개발 5대 원칙 (SOLID)

모든 개발이 그렇듯 프로젝트 초반에는 완벽하게 설계가 된것 같고 견고하게 구조를 다듬어 나아가는듯하게 개발이 되지만 개발을 하면 할수록 나의 코드와 구조의 틀이 점점 어긋나기 시작합

velog.io

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에 영향 주지만,

Employeeclient에게는 영향을 주지 않음. (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 고정

 

다형성 구성, Abstract Coupling

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가 존재한다면,

STsubtype (S <: T) 이.

 

=> T로 정의된 모든 프로그램 P에 대해,

o2o1(서브타입)으로 대치할 경우 P의 행동(behavior)이 변하지 않는다(unchanged).

 

Behavior Subtyping (대치)

슈퍼타입의 행동 - 언어가 지원을 X -> 프로그래머가 해당 행동을 지켜야함.

대치 - 언어가 지원

 

 

Square의 표현

(좌)경우 1, (우) 경우 2

  • 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의 상속

- 모든 parentclass invariant는 상속된다. 즉 클래스(child)에게 적용된다.

- Misbehaving descendant

재정의 시 parents’ invariants를 위반하는 경우가 발생할 수 있다.

 

 

 

 

 

11. Interface Segregation Principle (ISP)

의미

- 각 client 에 특화된 작은 interface 를 만들기

- 인터페이스는 오직 사용자가 필요로 하는 메소드들만 제공하게 하기

 

각각의 인터페이스 결집도 높게

 

 

댓글