00193 20151210 자바 GoF 디자인패턴 수업 4일차 - AngryQA/blog GitHub Wiki
자바 GoF 디자인패턴 수업 4일차
AngryQA | 2015-12-10 목요일 오전 9:7 | IT/개발전반 | 원본
1. 리플렉션
Reflection(Instropection)
: Class 이름만으로 클래스의 정보(필드, 메스도를 찾거나 객체를 생성할 수 있는 기능
1-1 class 얻기 방법 세가지
ㅁ 객체를 통해서
ㅁ 타입을 통해서
ㅁ 문자열 통해서
1-2 class 속성 얻기
ㅁ 퍼블릭인지, 추상인지 확인할수 있음,
1-3 메소드 속성 얻기
ㅁ 가능함함함함
[#M_더보기|접기|
// C++ : RTTI
import java.lang.reflect.Method; import java.lang.reflect.Modifier;
// Reflection(Instropection) // : Class 이름만으로 클래스의 정보(필드, 메소드를 찾거나 // 객체를 생성할 수 있는 기능 public class Example1 { public static void main(String[] args) throws Exception { // 1. Class 얻기 방법 세가지 // 1) 객체를 통해서 얻기 Person person = new Person("Tom", 42); Class personClass = person.getClass();
// 2) 타입을 통해서 얻기
personClass = Person.class;
// 3) 문자열 통해서 얻기
personClass = Class.forName("Person");
// 2\. Class 속성 얻기
int modes = personClass.getModifiers();
System.out.println("public : " + Modifier.isPublic(modes));
System.out.println("abstract : " + Modifier.isAbstract(modes));
System.out.println("final : " + Modifier.isFinal(modes));
// 3\. Method 속성 얻기
Method[] methods = personClass.getMethods();
for (Method m : methods) {
System.out.println(m.getName());
System.out.println(m.getParameterCount());
Class[] params = m.getParameterTypes();
for (Class p : params) {
System.out.println("\t" + p.getName());
}
}
} }
final class Person { private String name; private int age;
public Person() { name = ""; age = 0; }
public Person(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + ''' + ", age=" + age + '}'; }
}
_M#]
2. Private Filed 값 읽기/쓰기
리플랙션으로 private 필드 멤버 접근이 가능하다... 깡패임
흐음 자바의 객체의 은닉성을 파괴하므로 특수한 목적으로 활용해야한다.
[#M_더보기|접기|
import java.lang.reflect.Field;
public class Example2 { public static void main(String[] args) throws Exception { Point point = new Point();
// 1\. Public Field 값 읽기 / 쓰기
Class pointClass = point.getClass();
Field xField = pointClass.getField("x");
Field yField = pointClass.getField("y");
System.out.println(xField.get(point) + ", " +
yField.get(point));
xField.setInt(point, 10);
yField.setInt(point, 20);
System.out.println(xField.get(point) + ", " +
yField.get(point));
//----------------------------
// 2\. private field 읽기 / 쓰기
Field zField = pointClass.getDeclaredField("z");
System.out.println(zField.getName());
zField.setAccessible(true);
zField.setInt(point, 100);
System.out.println("z : " + zField.get(point));
} }
class Point { public int x; public int y;
private int z;
}
_M#]
3. 리플렉션을 통한 객체생성
ㅁ 인자있는 생성자를 생성하는방법
4. kvc(키벨류코딩)
리프렉션사용하는 훌륭한 사례
[#M_더보기|접기|
import java.lang.reflect.Field;
// KVC(Key-Value Coding) /* public class Example4 { public static void main(String[] args) { Person person = new Person();
String input = "name";
String value = "Tom";
// 문제점 : 필드를 추가할 때마다, 기존 코드는 계속 수정되어야 한다.
// OCP를 만족하지 못한다.
if (input.equals("name"))
person.setName(value);
else if (input.equals("phone"))
person.setPhone(value);
if (input.equals("name"))
value = person.getName();
else if (input.equals("phone"))
value = person.getPhone();
} } */
// KVC를 이용한다면, OCP를 만족한다. class Example4 { public static void main(String[] args) { Person person = new Person();
String input = "name";
String value = "Tom";
person.setValue(input, value);
value = (String) person.getValue(input);
} }
class Person { private String name; private String phone;
private int age;
public Person() { this.name = ""; this.phone = ""; }
public Person(String name, String phone) { this.name = name; this.phone = phone; }
public void setValue(String key, Object value) { Class clazz = Person.class; try { Field field = clazz.getField(key); field.set(this, value); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } }
public Object getValue(String key) { Class clazz = Person.class; try { Field field = clazz.getField(key); return field.get(this); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); }
return null;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
@Override public String toString() { return "Person{" + "name='" + name + ''' + ", phone='" + phone + ''' + '}'; } }
_M#]
5. 팩토리(객체생성을하는 클래스)
계속 수정되어야하는데.. 이걸 리플랙션으로 해소할수있다.
C++의 프랜드랑 비슷해짐
[#M_더보기|접기|
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;
class Shape {} class Rect extends Shape { private Rect() {} } class Circle extends Shape { private Circle() {} } class Triangle extends Shape { private Triangle() {} }
public class Example5 { public static void main(String[] args) { ShapeFactory factory = new ShapeFactory();
Shape rect = factory.create("Rect");
Shape circle = factory.create("Circle");
// rect = new Rect();
} }
// 팩토리(객체 생성을 하는 클래스) // 장점 : 객체 생성에 관한 코드를 한곳에 모아 관리할 수 있다. // 문제점 // 새로운 도형이 추가됨에 따라 팩토리의 코드는 수정되어야 한다. class ShapeFactory { Map classMap = new HashMap();
public Shape create(String name) { Class shapeClass = null; try { shapeClass = classMap.get(name); if (shapeClass == null) { shapeClass = Class.forName(name); classMap.put(name, shapeClass); }
Constructor cons = shapeClass.getDeclaredConstructors()[0];
cons.setAccessible(true);
return (Shape) cons.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
} }
/* class ShapeFactory { public Shape create(String name) { switch (name) { case "Rect": return new Rect(); case "Circle": return new Circle(); case "Triangle": return new Triangle(); }
return null;
} } */
_M#]
6. 리플렉션 성능
캐쉬화해야한다.
[#M_더보기|접기|
// Reflection 성능 // 1. 염려할 만큼 성능저하를 가져오지 않는다. // 2. 대량의 if/else, switch 대신 // 잘 설계된 리플렉션은 객체 지향 철학을 어기지 않으면서도 // 더 좋은 성능을 발휘할 수도 있다. public class Example6 {
public static void main(String[] args) throws Exception { System.out.print("Regular : "); doRegular();
System.out.print("Reflection : ");
doReflection();
System.out.print("Reflection(Point.class) : ");
doReflection2();
}
public static void doReflection2() throws Exception { long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; ++i) {
Point point = Point.class.newInstance();
point.print();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void doReflection() throws Exception { long start = System.currentTimeMillis();
Class pointClass = Class.forName("Point");
for (int i = 0; i < 1000000; ++i) {
Point point = (Point) pointClass.newInstance();
point.print();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void doRegular() throws Exception { long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; ++i) {
Point point = new Point();
point.print();
}
System.out.println(System.currentTimeMillis() - start);
} }
class Point { private int x; private int y;
public void print() { }
}
_M#]
7. Annotation(어노테이션)
이펙티브 유닛테스트 추천 + 테스트 패턴 책
아래는 Junit 내부 구현방법을 직접 코딩해본사례
Juint은 어노테이션 + 리플렉션의 좋은 사례
[#M_더보기|접기|
// Annotation(어노테이션) // 문학적 프로그래밍 // : 코드를 작성할 때 기계가 이해하기 쉬운 측면보다, 사람이 이해하기 쉬운 // 측면으로 작성해야 한다. // 주석(Comment) : 비공식적이고 임시적이다. // 정의 : 자바의 각 요소(클래스, 필드, 메소드, 매개변수) 각 가질 수 있는 // 주석 리소스 import java.lang.annotation.*; import java.lang.reflect.Method;
// JUnit : Annotation + Reflection @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Test { boolean enabled() default true; }
public class Example7 {
@Test
public void testA() { System.out.println("do Something"); }
@Test(enabled = false)
public void testB() {
System.out.println("do Something");
throw new RuntimeException("failed");
}
@Test(enabled = false)
public void testC() {
System.out.println("do Something");
throw new RuntimeException("failed");
}
public static void main(String[] args) {
System.out.println("Testing....");
int count = 0;
int passed = 0;
int failed = 0;
int ignore = 0;
Class testClass = Example7.class;
for (Method method : testClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
// System.out.println(method.getName());
++count;
Annotation annotation
= method.getAnnotation(Test.class);
Test test = (Test) annotation;
if (test.enabled()) {
try {
method.invoke(testClass.newInstance());
++passed;
System.out.printf("Test '%s' - passed\n",
method.getName());
} catch (Throwable e) {
++failed;
System.out.printf("Test '%s' - failed\n",
method.getName());
}
} else {
++ignore;
System.out.printf("Test '%s' - ignored\n",
method.getName());
}
}
}
System.out.printf("Result - total:%d " +
"passed:%d failed:%d ignored:%d",
count, passed, failed, ignore);
}
}
_M#]
8. 재정의될수 있는 메소드를 생성자 내부에서 호출 하면 안됨
[#M_더보기|접기|
public class Example8 { public static void main(String[] args) { MyPerson p = new MyPerson(); System.out.println(p.age()); System.out.println(p.name()); } }
// 1. 재정의 될 수 있는 메소드를 생성자 내부에서 호출하면 안된다. // 2. 부모의 private 메소드는 재정의 할 수 없다. // 3. final 메소드는 재정의의가 불가능하다.
class Person { private String name; public Person() { initialize(); }
private void initialize() { name = "SCV"; }
public String name() { return name; } }
class MyPerson extends Person { private int age; public MyPerson() { initialize(); }
private void initialize() { age = 10; }
public int age() { return age; }
}
_M#]
9. Finally 블록은 try 블록을 벗어나면 무조건 수행
그러므로 흐름제어! 쓰면 안됨 return, break, contiume등..
10. 생성자 전에 인스턴스가 초기화 되서 문제가 있음.. .. 그러므로 인스턴스 관련 문제 해결을 생성자에서 해줘야함.,
[#M_더보기|접기|
public class Example10 { public static void main(String[] args) { Car p = new Car(); } }
class Engine { }
// 1. 객체가 생성될 때, 생성자 전에 인스턴스 필드 초기화가 발생한다. class Car { /* private Car instance = new Car(); public Car() throws Exception { throw new Exception("Failed to Create"); } */
// 2. 인스턴스 필드의 초기화에서 발생한 예외는 생성자에서 // 처리해야 한다. private static Class engineClass = Engine.class;
private Engine engine = newEngine(); private static Engine newEngine() { try { return engineClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }
/* private Engine engine = engineClass.newInstance(); public Car() throws Exception { } */
}
_M#]
11. 파일 I/O에 대해서
최신 자바 일수록 빠른 API를 제공한다.
그냥(byte) -> 버퍼 -> NIO1->**NIO2 **
실행결과
[#M_더보기|접기|
import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption;
public class Example11 { public final static String from = "C:\sample.jpg"; public final static String to = "C:\to.jpg";
public static void main(String[] args) throws Exception {
long startTime = System.nanoTime();
copy(from, to);
long endTime = System.nanoTime();
long totalTime = (endTime - startTime) / 1000000L;
System.out.println("copy1 : " + totalTime + "ms");
startTime = System.nanoTime();
copy2(from, to);
endTime = System.nanoTime();
totalTime = (endTime - startTime) / 1000000L;
System.out.println("copy2(buffter) : " + totalTime + "ms");
startTime = System.nanoTime();
copy3(from, to);
endTime = System.nanoTime();
totalTime = (endTime - startTime) / 1000000L;
System.out.println("copy3(NIO) : " + totalTime + "ms");
startTime = System.nanoTime();
copy4(from, to);
endTime = System.nanoTime();
totalTime = (endTime - startTime) / 1000000L;
System.out.println("copy4(NIO2) : " + totalTime + "ms");
}
// 1\. 20s
public static void copy(String from, String to) throws IOException { try (FileInputStream fis = new FileInputStream(from); FileOutputStream fos = new FileOutputStream(to)) { while (true) { int data = fis.read(); if (data == -1) // EOF break; fos.write(data); } } }
// 2\. Buffered Version : 324ms vs 30ms
public static void copy2(String from, String to) throws IOException { try (FileInputStream fis = new FileInputStream(from); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(to); BufferedOutputStream bos = new BufferedOutputStream(fos)) { while (true) { int data = bis.read(); if (data == -1) // EOF break; bos.write(data); } } }
// 3\. NIO(New IO) - 1.4 - 33ms
public static void copy3(String from, String to) throws IOException { try (FileInputStream is = new FileInputStream(from); FileOutputStream os = new FileOutputStream(to)) {
FileChannel fis = is.getChannel();
FileChannel fos = os.getChannel();
ByteBuffer buf = ByteBuffer.allocateDirect(4096);
while (true) {
int ret = fis.read(buf);
if (ret == -1)
break;
buf.flip();
fos.write(buf);
buf.clear();
}
}
}
// Java 7 : NIO2 - 25ms
public static void copy4(String from, String to) throws IOException { Files.copy(Paths.get(from), Paths.get(to), StandardCopyOption.REPLACE_EXISTING); }
}
_M#]
12. 비동기 io 모델,, 비동기 io를 통한 파일 복사
[#M_더보기|접기|
import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;
// NIO2 - Java 1.7 // 비동기 IO를 통한 파일 복사 public class Example12 { public final static String from = "/Users/ourguide/Dropbox/sample.jpg"; public final static String to = "/Users/ourguide/Desktop/to.jpg";
public static void main(String[] args) throws Exception {
ByteBuffer buf = ByteBuffer.allocateDirect(4096);
AsynchronousFileChannel fis
= AsynchronousFileChannel.open(Paths.get(from));
CompletionHandler handler
= new CompletionHandler() {
int pos;
FileChannel fos = FileChannel.open(Paths.get(to),
StandardOpenOption.WRITE,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
@Override
public void completed(Integer result, Object attachment) { System.out.println(result + "bytes read"); if (result == -1) return;
buf.flip();
try {
fos.write(buf, pos);
pos += result;
buf.clear();
fis.read(buf, pos, null, this);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) { exc.printStackTrace(); } };
fis.read(buf, 0, null, handler);
System.in.read();
}
}
_M#]
13.