00194 20151211 자바 GoF 디자인패턴 수업 5일차 - AngryQA/blog GitHub Wiki

자바 GoF 디자인패턴 수업 5일차

AngryQA | 2015-12-11 금요일 오전 9:49 | IT/개발전반 | 원본

1. NIO2 - 파일시스템 와치 서비스

파일 전송등을 서비스와 이벤트로 관리할 수 있네.. 

[#M_더보기|접기|

import java.nio.file.*;

// NIO2 - FileSystem Watch Service public class Example1 { public static final String TARGET_PATH = "/Users/ourguide/Downloads/";

public static void main(String[] args) throws Exception { Path target = Paths.get(TARGET_PATH);

 // 1\. WatchService 생성
 try (WatchService fsWatchService
          = FileSystems.getDefault().newWatchService()) {
   // 2\. Watch 이벤트 등록
   target.register(fsWatchService,
       StandardWatchEventKinds.ENTRY_CREATE,
       StandardWatchEventKinds.ENTRY_DELETE,
       StandardWatchEventKinds.ENTRY_MODIFY);

   while (true) {
     WatchKey key = fsWatchService.take();
     for (WatchEvent event : key.pollEvents()) {
       System.out.printf("Received %s event for file : %s\n",
           event.kind(), event.context());
     }

     boolean isReset = key.reset();
     if (!isReset)
       break;
   }
 }

}

_M#]

이제부턴 진짜 디자인 패턴 

2. GoF's 디자인 패턴

 : 에릭 감마(eclipse, juint)

ㅁ 클래스 라이브러리보다 더 일반적인 관점

ㅁ 클래스 라이브러리에 많이 사용되고 있음

ㅁ 목적 : 하나의 프로그램을 재사용 가능하게하는것.. 

ㅁ 디자인 패턴 남용 No No~

ㅁ 리팩토링은 방법.. 디자인패턴은 목표??

테스트 -> 리팩토링 -> 테스트

리팩토링 이전과 이후.. 동작이 보장되어야한다. 

즉 리팩토링 하기전에는 유닛테스트가 완벽하게 작성되어있어야함.

단위테스트(리그렉션테스트)가 충분히 확보되어야함.. 

TDD(테스트주도개발)

1. 단위테스트 작성 -> 2,. 모듈 작성 -> 3. 리팩토링

SOLID 원칙 (객체지향의 5원칙) - 로버트 마틴

SRP : 단일 책임

OCP : 개방 폐쇄

LSP : 리스코프 대체

ISP : 인터페이스 분리

DIP : 의존관계 역전 

유닛테스트를 작성할때도 SOLID 설계원칙을 따르는게 좋다.

이책에도 그리 나오는듯 

3. 도형 편집기로 배우는 객체 지향의 원리

모든 도형을 타입, 부모 클래스필요.. 

모든 자식의 공통된 규칙은 부모로부터 와야함..(LSP)

[#M_더보기|접기|

// 도형 편집기로 배우는 객체 지향의 원리

// 1. 모든 도형을 타입으로 만들면 편하다. // 2. 모든 도형의 공통의 부모가 있다면 모든 도형을 묶어서 관리할 수 있다. // 3. 모든 자식의 공통된 특징은 부모로부터 와야 한다. // : 그래야 부모의 레퍼런스로 자식을 사용할 때 해당하는 특징을 // 사용할 수 있다. // LSP(Liskov Substitution Principle)

import java.util.ArrayList; import java.util.List; import java.util.Scanner;

public class Example3 { public static void main(String[] args) { List shapes = new ArrayList();

 Scanner scanner = new Scanner(System.in);
 while (true) {
   int cmd = scanner.nextInt();
   if      (cmd == 1) shapes.add(new Rect());
   else if (cmd == 2) shapes.add(new Circle());
   else if (cmd == 9) {
     for (Shape shape : shapes)
       shape.draw();
   }
 }

} }

abstract class Shape { public abstract void draw(); }

class Rect extends Shape { public void draw() { System.out.println("Rect draw"); } }

class Circle extends Shape { public void draw() { System.out.println("Circle draw"); } }

_M#]

4. 복사 구현

Prototype Pattern

다형성을 통해 객체를 구현하는 방법(OCP)

5. 공통성과 가변성 분리

Template Method Pattern

변하지 않는 전체 흐름은 부모, 변하는 것은 자식 클래스가 재정의 하는 설계 방법(추상클래스도 사용)

동기화 구현 구현 해제를 개발할때 유용함

[#M_더보기|접기|

import java.util.ArrayList; import java.util.List; import java.util.Scanner; // 도형 편집기로 배우는 객체 지향의 원리 // 4. 새로운 기능이 추가되어도 기존 코드는 수정되면 안된다. // OCP(Open Close Principle)

// 5. 다형성은 OCP를 만족한다. // 6. 프로토타입 패턴(Prototype Pattern) // : 다형성을 통해 객체를 복사하는 방법 // Java - Cloneable, C# - ICloneable // ObjC - Copyable // 7. Replace type code with Polymorphism

public class Example4 { public static void main(String[] args) { List shapes = new ArrayList();

 Scanner scanner = new Scanner(System.in);
 while (true) {
   int cmd = scanner.nextInt();
   if (cmd == 1) shapes.add(new Rect());
   else if (cmd == 2) shapes.add(new Circle());
   else if (cmd == 8) {
     System.out.print("index :  ");
     int index = scanner.nextInt();

     shapes.add(shapes.get(index).clone());

     // Rect, Circle 인지 알 수 있을까요?
     // shapes.get(index);
     /*
     switch (shapes.get(index).type) {
       case 1:
         // Rect 복사
         break;
       case 2:
         // Circle 복사
         break;
     }
     */

   } else if (cmd == 9) {
     for (Shape shape : shapes)
       shape.draw();
   }
 }

} }

abstract class Shape implements Cloneable { public void draw() { // synchronized (Shape.class) { drawImpl(); //} }

protected abstract void drawImpl(); // 8. 공통성과 가변성의 분리 // 변해야 하는 것과 변하지 않는 것은 분리되어야 한다. // => 변하지 않는 전체 흐름은 부모 클래스가 제공하고, 변하는 것은 // 자식 클래스가 재정의 하는 설계 방법. // Template Method Pattern // C++ : NVI(Non Virtual Interface)

// 9. DRY 원칙 (Do not Repeat Yourself)

@Override public Shape clone() { try { return (Shape) super.clone(); } catch (CloneNotSupportedException e) { } return null; } }

class Rect extends Shape { @Override public Rect clone() { return (Rect) super.clone(); }

protected void drawImpl() { System.out.println("Rect draw"); } }

class Circle extends Shape { @Override public Circle clone() { return (Circle) super.clone(); }

protected void drawImpl() { System.out.println("Circle draw"); } 

}

_M#]

6. Adapter Pattern

기존 클래스의 인터페이스를 변경해서 새로운 클래스처럼 보이는 것를 만드는것

ex) Vector를 상속받아 인터페이스만 작성 - 문제점은 백터의 다른 메소드를 사용할수있으니 문제다아아!

[#M_더보기|접기|

import java.util.EmptyStackException; import java.util.Vector;

public class Example5 { public static void main(String[] args) { Stack s = new Stack(); s.push(10); s.push(20); s.push(30); System.out.println(s.pop()); } }

// 1. Vector 가 이미 있다. // 2. 그런데 클라이언트가 stack 을 요구한다. // 1) 새롭게 다시 만들어 준다. // 2) 한쪽으로 데이터를 넣거나 빼면, 스택이다. Vector를 재사용하자. // 3. Adapter Pattern // : 기존 클래스의 인터페이스를 변경해서 클라이언트가 요구하는 // 새로운 클래스처럼 보이게 만드는 패턴 // 4. 라이브러리 설계자 관점 // 사용자들이 라이브러리를 쉽게 이용 할 수 있도록 만듬과 동시에 // 잘못 사용하도록 어렵게 만들어야 한다.

class Stack extends Vector { public void push(E object) { addElement(object); }

public E pop() { if (elementCount == 0) { throw new EmptyStackException(); } int index = --elementCount; E obj = (E)elementData[index]; elementData[index] = null; return obj; } }

_M#]

7. S/W의 재사용에는 상속과 포함이 있는데.. 포함이 좋을때가 많다

[#M_더보기|접기|

import java.util.EmptyStackException; import java.util.LinkedList; import java.util.List;

public class Example5 { public static void main(String[] args) { Stack s = new Stack(); s.push(10); s.push(20); s.push(30); System.out.println(s.pop()); } }

// 1. Vector 가 이미 있다. // 2. 그런데 클라이언트가 stack 을 요구한다. // 1) 새롭게 다시 만들어 준다. // 2) 한쪽으로 데이터를 넣거나 빼면, 스택이다. Vector를 재사용하자. // 3. Adapter Pattern // : 기존 클래스의 인터페이스를 변경해서 클라이언트가 요구하는 // 새로운 클래스처럼 보이게 만드는 패턴 // 4. 라이브러리 설계자 관점 // 사용자들이 라이브러리를 쉽게 이용 할 수 있도록 만듬과 동시에 // 잘못 사용하도록 어렵게 만들어야 한다.

// http://d.pr/n/1g2Mh /* class Stack extends Vector { public void push(E object) { addElement(object); }

public E pop() { if (elementCount == 0) { throw new EmptyStackException(); } int index = --elementCount; E obj = (E)elementData[index]; elementData[index] = null; return obj; } } */ // S/W 재사용에는 상속과 포함이 있다. // - 포함이 좋을 때가 많다. class Stack { private List data = new LinkedList();

public void push(E object) { data.add(object); }

public E pop() { if (data.isEmpty()) throw new EmptyStackException();

 int index = data.size() - 1;
 E obj = data.get(index);
 data.remove(index);

 return obj;

} }

_M#]

8. 자바 클래스 라이브러리에서 사용하고 있는 어댑터 디자인 패턴

ㅁ  Arrays.asList

ㅁ MVC

  1. 한 클래스의 인터페이스를 클라이언트에서 사용하고자

  하는 다른 인터페이스로 변환하는 설계 기법

 2) 이미 존재하는 클래스를 재사용하는 기법

[#M_더보기|접기|

// 자바 클래스 라이브리러에서 사용하고 있는 어댑터 디자인 패턴 // 1. Arrays.asList // 2. MVC

// 1) 한 클래스의 인터페이스를 클라이언트에서 사용하고자 // 하는 다른 인터페이스로 변환하는 설계 기법 // 2) 이미 존재하는 클래스를 재사용하는 기법

import java.util.Arrays; import java.util.List;

public class Example6 { public static void main(String[] args) { String[] s = { "hello", "hi", "good", "morning" }; List stringList = Arrays.asList(s);

} }

_M#]

9. 결합도..

강한결합

[#M_더보기|접기|

/ 결합도 : 클래스 간의 서로 다른 책임들이 얽혀 있는 상호 의존도 정도

class SmartPhone { public void takePicture() { System.out.println("Take picture with Smart Phone"); } }

class Camera { public void takePicture() { System.out.println("Take picture with Camera"); } }

class Person { public void useCamera(Camera camera) { camera.takePicture(); } }

public class Example7 { public static void main(String[] args) { Person person = new Person(); Camera camera = new Camera();

 person.useCamera(camera);

}

_M#]

약한결합

하나의 클래스가 다른 클래스를 사용할 때 부모인 인터페이스나 추상 클래스를 사용해야 접근한느것

  • 교체가능한 유연한 디자인의 핵심, 나중에 클래스가 추가되어도 기존 코드는 수정될 필요가 없다.

[#M_더보기|접기|

// 1. 카메라 사용자와 카메라 제작자 사이의 규칙을 먼저 설계하자. // (인터페이스, 계약서, 프로토콜) interface ICamera { void takePicture(); }

class Person { public void useCamera(ICamera p) { p.takePicture(); } }

// 2. "모든 카메라는 위의 인터페이스로부터 파생되어야 한다." 라고 하지말고 // "모든 카메라는 위의 인터페이스를 구현해야 한다" 라고 표현한다. class Camera implements ICamera { @Override public void takePicture() { System.out.println("Take picture with Camera"); } }

class SmartPhone implements ICamera { @Override public void takePicture() { System.out.println("Take picture with SP"); } }

// 핵심 : 교체 가능하려면 인터페이스가 필요하다. // 약한 결합(loosely coupling) : 하나의 클래스가 다른 클래스를 사용할 때 // 부모인 인터페이스나 추상 클래스를 사용해서 접근하는 것. // - 교체가능한 유연한 디자인의 핵심 // - 나중에 클래스가 추가되어도 기존 코드는 수정될 필요가 없다. // "OCP를 만족한다."

// DIP(Dependency Inversion Principle) // : 클라이언트는 구체 클래스가 아닌 추상 클래스나 인터페이스에 의존해야 한다. public class Example8 { public static void main(String[] args) { Person person = new Person(); Camera camera = new Camera(); SmartPhone smartPhone = new SmartPhone();

 person.useCamera(smartPhone);

} }

_M#]

  1. 인터페이스를 이용하면 교체 가능한 유연한 디자인을 가능하게 한다.

[#M_더보기|접기|

// 1. 인터페이스를 이용하면 교체 가능한 유연한 디자인을 가능하게 한다.

// 2. 범용 인터페이스 보다는 세분화된 인터페이스가 낫다. // ISP(Interface Segregation Principle) interface Calculator { int add(int a, int b); int sub(int a, int b); }

// List datas = new ArrayList(); // datas = new LinkedList(); // 4. 정적 팩토리 메소드를 이용하면 클라이언트가 구체적인 타입이 아닌 // 인터페이스를 통해 객체를 사용할 수 있도록 해준다. class CalculatorFactory { static class BasicCalculator implements Calculator { @Override public int add(int a, int b) { return a + b; }

 @Override
 public int sub(int a, int b) {
   return a - b;
 }

}

public static Calculator defaultCalculator() { return new BasicCalculator(); } }

public class Example9 { public static void main(String[] args) { // 3. 클라이언트는 구체 클래스가 아닌 인터페이스나 추상 클래스에 의존해야 // 한다. // BasicCalculator calculator = new BasicCalculator(); Calculator calculator = CalculatorFactory.defaultCalculator(); } }

_M#]

11.(JAVA8) 디폴트 메소드가 생긴 이유는 새로 구현할 API때문에 클래스가 깨지는 경우 방지(JAVA8)

JAVA8 (Default(defender)) method

[#M_더보기|접기|

interface Calculator {

int add(int a, int b); int sub(int a, int b);

// 6. 클라이언트가 곱셈(mul)의 연산을 요구한다. // JAVA 8 : default(defender) method // - 인터페이스 메소드의 기본 동작을 정의하는 메소드 // - 새로운 API의 추가로 인해 인터페이스의 변경으로 자식 클래스가 // 깨지는 문제를 최소화해준다. default int mul(int a, int b) { int n = 0; for (int i = 0 ; i < b ; ++i) n += add(n, a); return n; }

class DefaultCalculator implements Calculator { @Override public int add(int a, int b) { return a + b; }

 @Override
 public int sub(int a, int b) {
   return a - b;
 }

 @Override
 public int mul(int a, int b) {
   return a * b;
 }

}

// 5. 자바 8은 인터페이스 안에 정적 메소드를 두는 것을 허용한다. static Calculator defaultCalculator() { return new DefaultCalculator(); } }

public class Example10 { public static void main(String[] args) { Calculator calculator = Calculator.defaultCalculator(); } }

_M#]

12. (JAVA8)그런데! 저렇게구현하게됬는데... 인터페이스는 다중 상속이 가능한데.. 

문제가 생긴다.. 다이아몬드상속??

 이런경우의 규칙  자식이우선어언

[#M_// 1. 클래스의 재정의된 메소드가 인터페이스의 디폴트 메소드보다 우선이다.|접기|

// 1. 클래스의 재정의된 메소드가 인터페이스의 디폴트 메소드보다 우선이다.

interface A { default void hello() { System.out.println("inside A"); } }

public class Example11 implements A { @Override public void hello() { System.out.println("inside App"); }

public static void main(String[] args) { new Example11().hello(); } }

_M#][#M_2. 상속 관계를 가지는 인터페이스에서는 서브 인터페이스의 default 메소드가 우선된다.|접기|

// 2. 상속 관계를 가지는 인터페이스에서는 서브 인터페이스의 default 메소드가 // 우선된다. interface A { default void hello() { System.out.println("inside A"); } }

interface C extends A { default void hello() { System.out.println("inside C"); } }

interface B {}

public class Example12 implements C, B, A { public static void main(String[] args) { new Example12().hello(); } }

_M#][#M_3. 모호한 경우, 여러 인터페이스를 구현하는 클래스가 명시적으로 default 메소드를 오버라이드 해야 한다.|접기|

// 3. 모호한 경우, 여러 인터페이스를 구현하는 클래스가 // 명시적으로 default 메소드를 오버라이드 해야 한다.

interface A {

}

interface C extends A { default void hello() { System.out.println("inside C"); } }

interface B { default void hello() { System.out.println("inside B"); } }

public class Example13 implements C, B { @Override public void hello() { C.super.hello(); }

public static void main(String[] args) { new Example13().hello(); }

 

}

_M#]

템플릿메서드 패턴은 활용도가 있는듯.. 

20151211.zip

Attachments(1)