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_더보기|접기|

// http://d.pr/n/13Txu

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. 

20151210.zip

Attachments(1)