00195 20151214 자바 GoF 디자인패턴 수업 6일차 - AngryQA/blog GitHub Wiki

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

AngryQA | 2015-12-14 월요일 오후 4:54 | IT/개발전반 | 원본

Java8 in Action" 책 추천

// Swift, Scala, javascript, C++, C#

// => 선언적 프로그래밍(Declarative Programming)

람다에서 발전 -> Stream!(JAVA8) - 선언적 플밍

SQL하고 비슷해지는게 선언적 프로그래밍이다

내부 로직구현은 신경쓸 필요없이 실행결과만 체크하면 된다는것..

Select, sort 등등?

1. LineEdit

// 변하지 않는 코드에서 변하는 정책은 분리되어야 한다. // "공통성과 가변성의 분리"

[#M_더보기|접기|

// LineEdit

import javax.swing.; import java.awt.; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

class LineEdit { private JFrame frame; private StringBuilder sb = new StringBuilder();

public LineEdit() { frame = new JFrame(); frame.setSize(300, 50);

 Container container = frame.getContentPane();
 container.setLayout(new FlowLayout());

 JLabel label = new JLabel("");
 container.add(label);

 frame.addKeyListener(new KeyListener() {
   @Override
   public void keyTyped(KeyEvent e) {
     char c = e.getKeyChar();

     if (c == '\n') {
       sb = new StringBuilder();
       label.setText("");
     }

     sb.append(c);
     label.setText(sb.toString());
   }

   @Override
   public void keyPressed(KeyEvent e) {
   }

   @Override
   public void keyReleased(KeyEvent e) {
   }
 });

 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void show() { frame.setVisible(true); }

}

public class Example1 { public static void main(String[] args) { LineEdit lineEdit = new LineEdit(); lineEdit.show(); } 

}

_M#][#M_더보기|접기|

// LineEdit

// http://d.pr/n/fqBl import javax.swing.; import java.awt.; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

class LineEdit { private JFrame frame; private StringBuilder sb = new StringBuilder();

// 변하지 않는 코드에서 변하는 정책은 분리되어야 한다. // "공통성과 가변성의 분리" public LineEdit() { frame = new JFrame(); frame.setSize(300, 50);

 Container container = frame.getContentPane();
 container.setLayout(new FlowLayout());

 JLabel label = new JLabel("");
 container.add(label);

 frame.addKeyListener(new KeyListener() {
   @Override
   public void keyTyped(KeyEvent e) {
     char c = e.getKeyChar();

     if (c == '\n') {
       sb = new StringBuilder();
       label.setText("");
     }

     if (Character.isDigit(c)) {
       sb.append(c);
       label.setText(sb.toString());
     }
   }

   @Override
   public void keyPressed(KeyEvent e) {
   }

   @Override
   public void keyReleased(KeyEvent e) {
   }
 });

 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void show() { frame.setVisible(true); }

}

public class Example1 { public static void main(String[] args) { LineEdit lineEdit = new LineEdit(); lineEdit.show(); } }

_M#]

2. // 방법 1. 변하지 않는 부모 클래스가 정의하고, 변하는 것은 메소드로 자식이

// 재정의하는 설계 방법 - Template Method Pattern

[#M_더보기|접기|

// LineEdit // http://d.pr/n/1f8L2 import javax.swing.; import java.awt.; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

// 변하지 않는 코드에서 변하는 정책은 분리되어야 한다. // "공통성과 가변성의 분리" // 방법 1. 변하지 않는 부모 클래스가 정의하고, 변하는 것은 메소드로 자식이 // 재정의하는 설계 방법 - Template Method Pattern

class LineEdit { private JFrame frame; private StringBuilder sb = new StringBuilder();

protected boolean validate(char c) { return Character.isDigit(c); }

public LineEdit() { frame = new JFrame(); frame.setSize(300, 50);

 Container container = frame.getContentPane();
 container.setLayout(new FlowLayout());

 JLabel label = new JLabel("");
 container.add(label);

 frame.addKeyListener(new KeyListener() {
   @Override
   public void keyTyped(KeyEvent e) {
     char c = e.getKeyChar();

     if (c == '\n') {
       sb = new StringBuilder();
       label.setText("");
     }

     if (validate(c)) {
       sb.append(c);
       label.setText(sb.toString());
     }
   }

   @Override
   public void keyPressed(KeyEvent e) {
   }

   @Override
   public void keyReleased(KeyEvent e) {
   }
 });

 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void show() { frame.setVisible(true); } }

class AddressLineEdit extends LineEdit { @Override protected boolean validate(char c) { return true; } }

public class Example2 { public static void main(String[] args) { LineEdit lineEdit = new AddressLineEdit(); lineEdit.show(); } 

}

_M#][#M_더보기|접기|

// LineEdit

// http://d.pr/n/1f8L2 import javax.swing.; import java.awt.; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

// 변하지 않는 코드에서 변하는 정책은 분리되어야 한다. // "공통성과 가변성의 분리" // 방법 1. 변하지 않는 부모 클래스가 정의하고, 변하는 것은 메소드로 자식이 // 재정의하는 설계 방법 - Template Method Pattern

// http://d.pr/n/1cFYu // 문제점 // 1) 실행 시간에 정책을 변경할 수 없다. - 정적인 변경 // 2) 정책의 재사용성이 좋지 않다. class LineEdit { private JFrame frame; private StringBuilder sb = new StringBuilder();

protected boolean validate(char c) { return Character.isDigit(c); }

public LineEdit() { frame = new JFrame(); frame.setSize(300, 50);

 Container container = frame.getContentPane();
 container.setLayout(new FlowLayout());

 JLabel label = new JLabel("");
 container.add(label);

 frame.addKeyListener(new KeyListener() {
   @Override
   public void keyTyped(KeyEvent e) {
     char c = e.getKeyChar();

     if (c == '\n') {
       sb = new StringBuilder();
       label.setText("");
     }

     if (validate(c)) {
       sb.append(c);
       label.setText(sb.toString());
     }
   }

   @Override
   public void keyPressed(KeyEvent e) {
   }

   @Override
   public void keyReleased(KeyEvent e) {
   }
 });

 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void show() { frame.setVisible(true); } }

class AddressLineEdit extends LineEdit { @Override protected boolean validate(char c) { return true; } }

public class Example2 { public static void main(String[] args) { LineEdit lineEdit = new AddressLineEdit(); lineEdit.show(); } }

_M#]

3. 방법 2 변하는것을 인터페이스 기반 다른 클래스로 분리한다. "Strategy Pattern"

단점은 복잡해질 가능성이 너무 큼.

[#M_더보기|접기|

// LineEdit // http://d.pr/n/1f8L2 import javax.swing.; import java.awt.; import java.awt.event.KeyEvent; import java.awt.event.KeyListener;

// 방법 2. 변하는 것을 인터페이스 기반 다른 클래스로 분리한다. // "Strategy Pattern"

// 장점 // 1. 실행 시간에 정책을 교체하는 것이 가능하다. // 2. 정책의 재사용성이 높다. // : QValidator

interface IValidator { boolean validate(char c); }

class LineEdit { private JFrame frame; private StringBuilder sb = new StringBuilder(); private IValidator validator = new IValidator() { @Override public boolean validate(char c) { return true; } };

public void setValidator(IValidator v) { validator = v; }

public LineEdit() { frame = new JFrame(); frame.setSize(300, 50);

 Container container = frame.getContentPane();
 container.setLayout(new FlowLayout());

 JLabel label = new JLabel("");
 container.add(label);

 frame.addKeyListener(new KeyListener() {
   @Override
   public void keyTyped(KeyEvent e) {
     char c = e.getKeyChar();

     if (c == '\n') {
       sb = new StringBuilder();
       label.setText("");
     }

     if (validator.validate(c)) {
       sb.append(c);
       label.setText(sb.toString());
     }
   }

   @Override
   public void keyPressed(KeyEvent e) {
   }

   @Override
   public void keyReleased(KeyEvent e) {
   }
 });

 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void show() { frame.setVisible(true); } }

// 새로운 정책이 필요하면 새로운 클래스를 정의하면 된다. (정책 클래스) class DigitOnlyValidator implements IValidator { @Override public boolean validate(char c) { return Character.isDigit(c); } }

public class Example3 { public static void main(String[] args) { LineEdit lineEdit = new LineEdit(); lineEdit.setValidator(new DigitOnlyValidator());

 lineEdit.show();

}

_M#]

4. Policy Based Design - c++

[#M_더보기|접기|

// http://d.pr/n/1lSvz // Policy Based Design - C++ 에서 많이 이용된다. // - 동기화의 정책은 변경되어야만 한다.

// 정리 (= Objective-C) // 1. 자바의 제네릭은 타입 안전성을 위한 문법이다. // 2. 코드를 생성하지 않는다. interface ThreadModel { void lock(); void unlock(); }

class SingleThread implements ThreadModel { @Override public void lock() { }

@Override public void unlock() { } }

class MultiThread implements ThreadModel { @Override public void lock() { System.out.println("lock"); }

@Override public void unlock() { System.out.println("unlock"); } }

class List { private T threadModel;

public void setThreadModel(T model) { threadModel = model; }

public void insert(E item) { threadModel.lock(); //.... threadModel.unlock(); } }

public class Example4 { public static List datas = new List();

public static void main(String[] args) { // datas.setThreadModel(new MultiThread()); // datas.setThreadModel(new SingleThread()); } 

}

_M#]

5. 람다... 활용예제들

요즘 핫한 개념

익명클래스를 효과적으로 쓰고 싶어서.. 

[#M_더보기|접기|

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

// 자바의 메소드 안에서 변하는 정책을 분리하는 방법 // -> 정책을 캡슐화한다. // -> 동작 파라미터화

// 1. 해당하는 인터페이스를 구현하는 정책을 만든다. // 2. 한번만 사용되는 정책이라면, 익명 클래스로 만들면된다. // 3. 람다를 사용하면 좀더 간결하게 작성 가능하다.

public class Example5 { public static void main(String[] args) { List users = new ArrayList(); users.add(User.create("Tom", 42)); users.add(User.create("Ann", 18)); users.add(User.create("Scott", 15));

 // 이름을 통해서 데이터를 필터링하고 싶다.
 /*
 List result = User.filter(users, new NamePredicate());
 result = User.filter(users, new TeenPredicate());
 for (User e : result) {
   System.out.println(e);
 }
 */

 // 1\. 조건이 다른 곳에서 사용될 필요가 없다면, 익명 클래스를 사용하면 됩니다.
 List result = User.filter(users, new Predicate() {
   @Override
   public boolean test(User obj) {
     return obj.getName().equals("Tom");
   }
 });

 // 2\. 익명 클래스는 편리하지만 불필요하게 반복되어 코드의 가독성을 나쁘게하는
 //    요소입니다.
 // => 람다를 사용하면 위의 코드를 좀더 간결하게 작성하는 것이 가능합니다.

 //  람다(C++, C#, Java, ObjC) : 코드 조각을 함수의 인자로 전달하는 기술
 //  => 익명 클래스를 효과적으로 사용할 수 있다.
 result = User.filter(users, user ->
     user.getName().equals("Tom")
 );

 result = User.filter(users, user ->
   user.getAge() >= 10  && user.getAge()  < 20
 );

 for (User e : result) {
   System.out.println(e);
 }

} }

// 조건자 interface Predicate { boolean test(E obj); }

class TeenPredicate implements Predicate { @Override public boolean test(User obj) { return obj.getAge() >= 10 && obj.getAge() < 20; } }

class NamePredicate implements Predicate { @Override public boolean test(User obj) { return obj.getName().equals("Tom"); } }

class User { // 메소드에서 변하는 것이 있다면, 변하는 것을 캡슐화 해야 한다. // => 동작 파라미터화

public static List filter(List users, Predicate predicate) { List result = new ArrayList(); for (User user : users) { if (predicate.test(user)) result.add(user); }

 return result;

}

private String name; private int age;

public User(String name, int age) { this.name = name; this.age = age; }

public String getName() { return name; }

public void setName(String name) { this.name = name; }

public int getAge() { return age; }

public void setAge(int age) { this.age = age; }

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

public static User create(String name, int age) { return new User(name, age); } }

_M#][#M_더보기|접기|

import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List;

public class Example6 { public static void main(String[] args) { List users = User.users();

 Collections.sort(users, (o1, o2) ->
     Integer.compare(o1.getAge(), o2.getAge())
 );

 Collections.sort(users,
     Comparator.comparing(User::getName));

 for (User e : users) {
   System.out.println(e);
 }

 int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
 Utils.sort(arr, new Comparator() {
   @Override
   public int compare(Integer o1, Integer o2) {
     return o2.compareTo(o1);
   }
 });

 Utils.sort(arr, (o1, o2) -> o2.compareTo(o1));
 Utils.sort(arr, (o1, o2) -> Integer.compare(o1, o2));

 // 람다를 호출하는 구문이 동일한 시그니쳐를 가지는 메소드를 호출하는 구문과
 // 동일하다면, 간략하게 표현하는 것이 가능하다. - 메소드 레퍼런스
 // 메소드 레퍼런스 : 하나의 메소드를 참조하는 람다를 편리하게 사용하는 문법
 Utils.sort(arr, Integer::compare);

 for (int e : arr) {
   System.out.println(e);
 }

} }

class Utils { // 동작 파라미터화를 통해 변하는 정책을 외부로 분리해야 한다. // Collections.sort() static void sort(int[] arr, Comparator comparator) { int n = arr.length; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (comparator.compare(arr[i], arr[j]) > 0) { // if (arr[i] < arr[j]) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } }

class User { public static List users() { return Arrays.asList( User.create("Tom", 42), User.create("Ann", 15), User.create("Scott", 18) ); }

private String name; private int age;

public User(String name, int age) { this.name = name; this.age = age; }

public String getName() { return name; }

public void setName(String name) { this.name = name; }

public int getAge() { return age; }

public void setAge(int age) { this.age = age; }

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

public static User create(String name, int age) { return new User(name, age); } 

}

_M#]

6. 람다의 원리

인터페이스메소드가 1개일경우만 가능

=> 함수형 인터페이스

@function

// 람다의 원리

// 1. 람다를 사용하기 위해서는 인터페이스가 정의하고 있는

//    추상 메소드가 한개이어야 합니다.

//    => 함수형 인터페이스(Functional Interface)

// 2. @FunctionalInterface 를 이용하면, 함수형 인터페이스

//     조건이 성립하는지 여부를 컴파일 타임에 검증할 수 있다.

// 3. 람다는 익명의 클래스를 편리하게 사용하기 위해 제안된 문법입니다.

[#M_더보기|접기|

// 람다의 원리 // 1. 람다를 사용하기 위해서는 인터페이스가 정의하고 있는 // 추상 메소드가 한개이어야 합니다. // => 함수형 인터페이스(Functional Interface)

// 2. @FunctionalInterface 를 이용하면, 함수형 인터페이스 // 조건이 성립하는지 여부를 컴파일 타임에 검증할 수 있다.

// 3. 람다는 익명의 클래스를 편리하게 사용하기 위해 제안된 문법입니다.

@FunctionalInterface interface MyPredicate { boolean foo(int n);

default void goo() {} static void hoo() {} }

public class Example7 { static void foo(MyPredicate predicate) {}

public static void main(String[] args) { foo(n -> true);

 Runnable runnable;

 new Thread(() -> {
   System.out.println("Thread start!!");
 });

}

_M#]

7. 람다의 내부 구성

[#M_더보기|접기|

import java.util.function.Function;

// 람다는 익명 클래스를 편리하게 사용하기 위한 문법입니다. // => 하지만 람다는 익명 클래스와 다르게 동작합니다.

// 익명 클래스 문제점 // => 컴파일러는 익명 클래스에 대응하는 클래스 파일을 생성해야 한다. // : 클래스 파일을 사용하기 위해서는 클래스 파일을 로드하고 검증하는 작업이 필요하므로 // 프로그램 시작에 악영향을 미친다.

// invokedynamic을 통해서 람다를 구현하고 있다. // 1) 람다 표현식의 바디를 바이트 코드로 변환하는 작업을 별도로 수행한다. // 2) 람다를 처음 실행한 이후의 호출은 추가적인 비용없이 메소드를 호출 가능하다. // 3) 반복적으로 호출할 경우 익명 클래스보다 빠르게 동작한다.

// javap -p -c -v Example8.class public class Example8 { public static void main(String[] args) { // 1. 익명 클래스 Function f1 = new Function() { @Override public String apply(Object o) { return o.toString(); } };

 // 2\. 람다
 // Function f2 = o -> o.toString();
 Function f3 = Object::toString;

}

_M#]

8. Supplier : 생성자 레퍼런스를 저장 가능한 타입

[#M_더보기|접기|

import java.util.HashMap; import java.util.Map; import java.util.function.Supplier;

abstract class Shape {}

class Rect extends Shape {} class Circle extends Shape {} class Triangle extends Shape {}

// 생성자 레퍼런스 // : 객체를 생성하는 기본 생성자를 참조할 수 있는 문법

// Supplier : 생성자 레퍼런스를 저장가능한 타입 class ShapeFactory { private static Map map = new HashMap();

public static void registerShape(String name, Supplier supplier) { map.put(name, supplier); }

public static Shape create(String name) { Supplier p = map.get(name); if (p != null) return p.get();

 throw new RuntimeException("No such Shape  "  + name);

} }

/* class ShapeFactory { public static Shape create(String name) { switch (name) { case "Rect": return new Rect(); case "Circle": return new Circle(); case "Triangle" return new Triangle();

   default:
     throw new RuntimeException("no such Shape  "  + name);
 }

} } */

public class Example9 { public static void main(String[] args) { ShapeFactory.registerShape("Rect", Rect::new); ShapeFactory.registerShape("Circle", Circle::new); ShapeFactory.registerShape("Triangle", Triangle::new);

 Shape shape = ShapeFactory.create("Rect");
 shape = ShapeFactory.create("Circle");

}

_M#]

9. JAVA8의 Stream API!

뭔가 SQL처럼 명시적으로 코딩할수 있다.

원래 다른 언어는 구현 코드..

ParallelStream를 쓰면! 다중쓰레드로 알아서 돔..

짱이당..

C++에는 비슷한 LINQ

구현보다 결과에서 에러가 날것이니 결과만 분석하면 되겠네

[#M_더보기|접기|

import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.stream.Collectors;

// 1. 프로그램은 컬렉션을 만들고 처리하는 과정이 대부분이다. // 2. 사용자가 원하는 조건으로 데이터를 조직화하고 탐색을 수행한다. public class Example10 { public static void main(String[] args) { List points = Point.points();

 // => 명령형 언어의 기법으로 코드를 작성해야 한다.
 // x 가 10 이하인 좌표를 필터링해서 출력하고 싶다.
 // 1\. 필터링
 List results = new ArrayList();
 for (Point p : points) {
   if (p.getX() 
Attachments(1)