00199 20151217 자바 GoF 디자인패턴 수업 9일차 - AngryQA/blog GitHub Wiki

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

AngryQA | 2015-12-17 목요일 오전 10:44 | IT/개발전반 | 원본

// GoF 디자인 패턴(23가지) // - 생성(5가지) // Singleton - 오직 한개만의 객체를 생성하고, 어디서든 동일한 방법 // 으로 접근 가능하게 만들자. // Abstract Factory - 공장도 인터페이스 기반으로 설계하여 // 교체 가능하게 하자. // Factory Method - 변하는 것을 메소드로 분리하는데, 변하는 것이 // 객체 생성에 관련된 것이라면 // Builder - 동일한 구축 공정을 가지지만, 다른 표현을 가지는 // 객체를 만들 때 // Prototype - 다형성을 통한 복제 // 견본(Prototype)에 의한 생성

// - 구조(7가지) // - 행위(11가지)

1. 포장지 패턴

핵심은 포장지는 객체를 포함하지만.. 포장된 객체를 다시 포장 할수 있다.

포장지와 객체는 동일 부모 필요.

//재귀적 합성을 통한 복합 객체 구성 => 컴포지트 패턴

//재귀적 합성을 통한 기능의 추가 -> 데코레이터 패턴

자바에서 파일을 다루는 것은... 포장지패턴으로 되어 있다. 

파일 스트림 버퍼스트림  zip인풋스트림..

[#M_더보기|접기|

// 포함을 사용하면 동적인 기능추가가 가능하다.

// 핵심 // 포장지는 객체를 포함하지만 포장된 객체를 다시 포장할 수 있어야 한다. // => 포장지와 객체는 동일 부모가 필요하다. interface Item { void fire(); }

class SpaceShip implements Item { public void fire() { System.out.println("전방 미사일 발사"); } }

// 포장지의 공통의 특징을 부모로 제공하자. abstract class Decorator implements Item { private Item item;

public Decorator(Item item) { this.item = item; }

@Override public void fire() { item.fire(); } }

// - 재귀적 합성을 사용하는 디자인 패턴 2가지 // 재귀적 합성을 통한 복합 객체 구성 => 컴포지트 패턴 // 재귀적 합성을 통한 기능의 추가 => 데코레이터 패턴 class RightMissile extends Decorator { public RightMissile(Item item) { super(item); }

public void fire() { System.out.println("오른쪽 미사일 발사"); super.fire(); } }

class BackMissile extends Decorator { public BackMissile(Item item) { super(item); }

public void fire() { super.fire(); System.out.println("후방 미사일 발사"); } }

public class Example2 { public static void main(String[] args) { SpaceShip spaceShip = new SpaceShip(); RightMissile missile = new RightMissile(spaceShip); BackMissile backMissile = new BackMissile(missile);

 // 기능을 추가한 객체에 다시 기능을 추가할 수 있어야 한다.
 backMissile.fire();

}

_M#][#M_파일스트림|접기|

import java.io.BufferedInputStream;

import java.io.FileInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream;

public class Example3 { public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("a.zip"); BufferedInputStream bis = new BufferedInputStream(fis); ZipInputStream zis = new ZipInputStream(bis);

 ZipEntry ze;
 while ((ze = zis.getNextEntry()) != null) {
   System.out.println(ze.getName());

   int n;
   byte[] buff = new byte[1024];
   while ((n = zis.read(buff)) != -1) {
     System.out.print(new String(buff, 0, n));
   }
 }

} }

// a.zip 이라는 파일의 내용을 읽고 싶다. // 방법 1. 클래스 하나로 구현한다. // 방법 2. 세 개의 작은 문제로 나누어 처리한다.(분할 정복) // 1) 파일을 읽는다. - FileInputStream // 2) 버퍼링을 한다. - BufferedInputStream // 3) 압축을 푼다. - ZipInputStream

_M#]

2. 원도우 스타일에 대해

[#M_더보기|접기|

abstract class Button { abstract void show(); }

abstract class EditBox { abstract void show(); }

class XPButton extends Button { @Override void show() { System.out.println("XP Button"); } } class GTKButton extends Button { @Override void show() { System.out.println("GTK Button"); } }

class XPEditBox extends EditBox { @Override void show() { System.out.println("XP EditBox"); } }

class GTKEditBox extends EditBox { @Override void show() { System.out.println("GTK EditBox"); } }

public class Example4 { public static void main(String[] args) { Button button; EditBox editBox; if (args.length > 1 && args[1].equals("--style=XP")) { // XP 버튼 생성 button = new XPButton(); editBox = new XPEditBox(); } else { // GTK 버튼 생성 button = new GTKButton(); editBox = new GTKEditBox(); }

 button.show();
 editBox.show();

} }

// Window Style - Qt4 

// ./program --style=XP

_M#]

3. Abstract Factory Pattern

-> 공장도 인터페이스 기반으로 만을어서 교체 가능하게 하자

[#M_더보기|접기|

// Abstract Factory Pattern // : 추상적인 부품을 조합해서 추상적인 제품을 만든다. // => 공장도 인터페이스 기반으로 만들어서 교체 가능하게 하자. // : 관련된 제품군을 생성하기 위해 공장을 바로 만들지 말고 // 인터페이스부터 설계하자.

abstract class Factory { abstract Button createButton(); abstract EditBox createEditBox(); }

class XPFactory extends Factory { XPButton createButton() { return new XPButton(); } XPEditBox createEditBox() { return new XPEditBox(); } }

class GTKFactory extends Factory { GTKButton createButton() { return new GTKButton(); } GTKEditBox createEditBox() { return new GTKEditBox();} }

public class Example5 { public static void main(String[] args) { Factory factory = null; if (args.length > 1 && args[1].equals("--style=XP")) { factory = new XPFactory(); } else { factory = new GTKFactory(); }

 // 원하는 형태로 생성 후 사용
 Button button = factory.createButton();
 EditBox editBox = factory.createEditBox();
 button.show();
 editBox.show();

} }

abstract class Button { abstract void show(); }

abstract class EditBox { abstract void show(); }

class XPButton extends Button { @Override void show() { System.out.println("XP Button"); } } class GTKButton extends Button { @Override void show() { System.out.println("GTK Button"); } }

class XPEditBox extends EditBox { @Override void show() { System.out.println("XP EditBox"); } }

class GTKEditBox extends EditBox { @Override void show() { System.out.println("GTK EditBox"); } 

}

_M#]

4. 프로토타입 더 알아보기..

그리고 요새는 복제가 아닌 복사생성자를 사용한다.

[#M_더보기|접기|

import java.util.*;

// 견본에 의한 생성 class ShapeFactory { private static ShapeFactory INSTANCE = new ShapeFactory();

public static ShapeFactory getInstance() { return INSTANCE; }

private Map prototype = new HashMap();

public void registerPrototype(int type, Shape shape) { prototype.put(type, shape); }

public Shape createShape(int type) { if (!prototype.containsKey(type)) return null;

 return prototype.get(type).clone();

} }

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

 // 공장에 견본을 등록한다.
 ShapeFactory factory = ShapeFactory.getInstance();
 factory.registerPrototype(1, new Rect());
 //                           new Rect("빨간색, 크기5");
 factory.registerPrototype(2, new Circle());

 Scanner scanner = new Scanner(System.in);
 while (true) {
   int cmd = scanner.nextInt();
   if (cmd > 0  && cmd  < 5) {
     Shape shape = factory.createShape(cmd);
     shapes.add(shape);
   }
   else if (cmd == 9) {
     for (Shape shape : shapes)
       shape.draw();
   }
 }

} }

abstract class Shape implements Cloneable { public abstract void draw();

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

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

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

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

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

_M#][#M_복사생성자 예제|접기|

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;

// 객체의 복사가 목적이라면, Cloneable 보다 복사 생성자가 좋다. public class Example7 { public static void main(String[] args) { Map values = new HashMap(); Map copy = new HashMap(values);

 List values2 = new ArrayList();
 List copy2 = new ArrayList(values2);

}

_M#]

5. 책임의 전가 패턴

이벤트 발생시 처리되지 않는경우 다음 객체에게 전달

해당하는 이벤트가 처리 할수 있을때까지 고리에 따라 이벤트를전달한다.

이벤트 처리시 많이 사용한다.

GoF 디자인 패턴중 이벤트 관련 패턴은 2가지

1. 관찰자 패턴 - 이벤트 전파

2. 책임의 전가 - 다음객체에게 이벤트 넘김

[#M_더보기|접기|

// 책임의 전가(Chain of Responsibility) // : 이벤트 발생시 처리되지 않을 경우 다음 객체에게 전달하는 패턴 // -> 처리할 수 있을 때까지 고리에 따라 이벤트를 전달한다.

// 이벤트와 이벤트 처리 객체의 결합도를 줄일 수 있다.

// OS X 계열의 GUI 라이브러리(코코아, 코코아 터치) // - Touch 이벤트(Responder Chain) // : MyUIView -> UIView -> MyViewController // -> UIViewController -> AppDelegate

// MFC 메뉴 이벤트 // : View -> Document -> Frame -> App

abstract class Logger { public static final int DEBUG = 1; public static final int INFO = 2; public static final int ERROR = 3;

private int level;

public Logger(int level) { this.level = level; }

private Logger next;

public void setNext(Logger logger) { this.next = logger; }

// 아래 코드가 책임의 전가 패턴의 핵심 구현입니다. public void message(String message, int priority) { // 1. 권한이 잇으면 처리 if (priority 여러클래스를 사용해 하나의 작업을 한다면 하나로 묶자! 라는 개념

예제소스(비행기,숙소예약)

[#M_더보기|접기|

import java.lang.reflect.Field; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List;

// 2015-12-25 부터 2015-12-31 까지 여행을 가고 싶다. // 가용한 호텔과 비행기를 확인하고 싶다. public class Example11 { public static void main(String[] args) { List hotels = Hotel.hotels(); List availHotels = new ArrayList();

 LocalDate start = LocalDate.of(2015, 12, 25);
 LocalDate end = LocalDate.of(2015, 12, 31);
 for (Hotel hotel : hotels) {
   LocalDate from = hotel.from();
   LocalDate to = hotel.to();

   if ((from.isEqual(start) || from.isBefore(start))
       && (to.isEqual(end) || to.isAfter(end))) {
     availHotels.add(hotel);
   }
 }

 List flights = Flight.flights();
 List availFlights = new ArrayList();

 LocalDateTime startTime
     = LocalDateTime.parse("2015-12-25T00:00:00");
 for (Flight flight : flights) {
   LocalDateTime time = flight.time();
   if (time.isEqual(startTime)
       || time.isAfter(startTime)) {
     availFlights.add(flight);
   }
 }

 for (Hotel hotel : availHotels) {
   System.out.println(hotel);
   for (Flight flight : availFlights) {
     System.out.println("\t"  + flight);
   }
 }

} }

class Hotel { private final String name; private final LocalDate from; private final LocalDate to;

public Hotel(String name, LocalDate from, LocalDate to) { this.name = name; this.from = from; this.to = to; }

public String getName() { return name; }

public LocalDate from() { return from; }

public LocalDate to() { return to; }

@Override public String toString() { return "Hotel{" + "name='" + name + ''' + ", from=" + from + ", to=" + to + '}'; }

public static List hotels() { return Arrays.asList( new Hotel("힐튼 호텔", LocalDate.of(2015, 12, 1), LocalDate.of(2015, 12, 15)), new Hotel("메리어트 호텔", LocalDate.of(2015, 12, 31), LocalDate.of(2016, 12, 5)), new Hotel("하야트 호텔", LocalDate.of(2015, 12, 15), LocalDate.of(2016, 1, 15)) ); } }

class Flight { private final String name; private final LocalDateTime time;

public Flight(String name, LocalDateTime time) { this.name = name; this.time = time; }

public String getName() { return name; }

public LocalDateTime time() { return time; }

@Override public String toString() { return "Flight{" + "name='" + name + ''' + ", time=" + time + '}'; }

public static List flights() { return Arrays.asList( new Flight("대한항공-0", LocalDateTime.parse("2015-12-01T17:00:00")), new Flight("대한항공-1", LocalDateTime.parse("2015-12-10T06:00:00")), new Flight("아시아나-0", LocalDateTime.parse("2015-12-20T08:00:00")), new Flight("아시아나-1", LocalDateTime.parse("2015-12-30T21:00:00")) ); }

}

_M#]

Facade(퍼사드 패턴)

패턴 적용 소스

[#M_더보기|접기|

import java.time.LocalDate;

import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List;

class HotelBooker { private List hotels;

public HotelBooker(List hotels) { this.hotels = hotels; }

public List getHotelsFor(LocalDate start, LocalDate end) { List result = new ArrayList(); for (Hotel hotel : hotels) { LocalDate from = hotel.from(); LocalDate to = hotel.to();

   if ((from.isEqual(start) || from.isBefore(start))
       && (to.isEqual(end) || to.isAfter(end))) {
     result.add(hotel);
   }
 }
 return result;

} }

class FlightBooker { private List flights;

public FlightBooker(List flights) { this.flights = flights; }

public List getFlightsFor(LocalDateTime startTime) { List result = new ArrayList(); for (Flight flight : flights) { LocalDateTime time = flight.time(); if (time.isEqual(startTime) || time.isAfter(startTime)) { result.add(flight); } } return result; } }

class BookInfo { final Hotel hotel; final Flight flight;

public BookInfo(Hotel hotel, Flight flight) { this.hotel = hotel; this.flight = flight; }

@Override public String toString() { return "BookInfo{" + "hotel=" + hotel + ", flight=" + flight + '}'; } }

// Facade(퍼사드) 패턴 // : 하위 시스템의 복잡함을 단순화시켜주는 상위 클래스(중간층)를 제공하자.

// => 여러 클래스를 사용해서 하나의 작업을 한다면, // 그 복잡함을 하나로 묶어주자.

class TravelBooker { private HotelBooker hotelBooker = new HotelBooker(Hotel.hotels()); private FlightBooker flightBooker = new FlightBooker(Flight.flights());

List getBookInfos(LocalDateTime start, LocalDateTime end) {

 List availHotels
     = hotelBooker.getHotelsFor(start.toLocalDate(),
     end.toLocalDate());
 List availFlights
     = flightBooker.getFlightsFor(start);

 List bookInfos = new ArrayList();
 for (Hotel hotel : availHotels) {
   for (Flight flight : availFlights) {
     BookInfo bi = new BookInfo(hotel, flight);
     bookInfos.add(bi);
   }
 }

 return bookInfos;

}

}

// 2015-12-25 부터 2015-12-31 까지 여행을 가고 싶다. // 가용한 호텔과 비행기를 확인하고 싶다. public class Example12 { public static void main(String[] args) { LocalDateTime start = LocalDateTime.parse("2015-12-25T08:00"); LocalDateTime end = LocalDateTime.parse("2015-12-31T00:00");

 TravelBooker travelBooker = new TravelBooker();
 List bookInfos = travelBooker.getBookInfos(start, end);

 for (BookInfo book : bookInfos) {
   System.out.println(book);
 }

} }

class Hotel { private final String name; private final LocalDate from; private final LocalDate to;

public Hotel(String name, LocalDate from, LocalDate to) { this.name = name; this.from = from; this.to = to; }

public String getName() { return name; }

public LocalDate from() { return from; }

public LocalDate to() { return to; }

@Override public String toString() { return "Hotel{" + "name='" + name + ''' + ", from=" + from + ", to=" + to + '}'; }

public static List hotels() { return Arrays.asList( new Hotel("힐튼 호텔", LocalDate.of(2015, 12, 1), LocalDate.of(2015, 12, 15)), new Hotel("메리어트 호텔", LocalDate.of(2015, 12, 31), LocalDate.of(2016, 12, 5)), new Hotel("하야트 호텔", LocalDate.of(2015, 12, 15), LocalDate.of(2016, 1, 15)) ); } }

class Flight { private final String name; private final LocalDateTime time;

public Flight(String name, LocalDateTime time) { this.name = name; this.time = time; }

public String getName() { return name; }

public LocalDateTime time() { return time; }

@Override public String toString() { return "Flight{" + "name='" + name + ''' + ", time=" + time + '}'; }

public static List flights() { return Arrays.asList( new Flight("대한항공-0", LocalDateTime.parse("2015-12-01T17:00:00")), new Flight("대한항공-1", LocalDateTime.parse("2015-12-10T06:00:00")), new Flight("아시아나-0", LocalDateTime.parse("2015-12-20T08:00:00")), new Flight("아시아나-1", LocalDateTime.parse("2015-12-30T21:00:00")) ); }

}

_M#]

서버 생성시에도 커넥터, 바인딩등이 필요한데 이걸 하나로 줄여서 만들어줄수도 있겠다시픔..

20151217.zip

Attachments(1)