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()