delegate

|

https://stackoverflow.com/questions/44912/java-delegates

 

Depending precisely what you mean, you can achieve a similar effect (passing around a method) using the Strategy Pattern.

Instead of a line like this declaring a named method signature:

// C#
public delegate void SomeFunction();

declare an interface:

// Java
public interface ISomeBehaviour {
   void SomeFunction();
}

For concrete implementations of the method, define a class that implements the behaviour:

// Java
public class TypeABehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeA behaviour
   }
}

public class TypeBBehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeB behaviour
   }
}

Then wherever you would have had a SomeFunction delegate in C#, use an ISomeBehaviour reference instead:

// C#
SomeFunction doSomething = someMethod;
doSomething();
doSomething = someOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

With anonymous inner classes, you can even avoid declaring separate named classes and almost treat them like real delegate functions.

// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

SomeMethod(new ISomeBehaviour() { 
   @Override
   public void SomeFunction() {
      // your implementation
   }
});

This should probably only be used when the implementation is very specific to the current context and wouldn't benefit from being reused.

And then of course in Java 8, these do become basically lambda expressions:

// Java 8
SomeMethod(() -> { /* your implementation */ });

-------------------------------

 

No, but they're fakeable using proxies and reflection:

  public static class TestClass {
      public String knockKnock() {
          return "who's there?";
      }
  }

  private final TestClass testInstance = new TestClass();

  @Test public void
  can_delegate_a_single_method_interface_to_an_instance() throws Exception {
      Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

The nice thing about this idiom is that you can verify that the delegated-to method exists, and has the required signature, at the point where you create the delegator (although not at compile-time, unfortunately, although a FindBugs plug-in might help here), then use it safely to delegate to various instances.

See the karg code on github for more tests and implementation.

 

--------------------------------

 

I have implemented callback/delegate support in Java using reflection. Details and working source are available on my website.

How It Works

There is a principle class named Callback with a nested class named WithParms. The API which needs the callback will take a Callback object as a parameter and, if neccessary, create a Callback.WithParms as a method variable. Since a great many of the applications of this object will be recursive, this works very cleanly.

With performance still a high priority to me, I didn't want to be required to create a throwaway object array to hold the parameters for every invocation - after all in a large data structure there could be thousands of elements, and in a message processing scenario we could end up processing thousands of data structures a second.

In order to be threadsafe the parameter array needs to exist uniquely for each invocation of the API method, and for efficiency the same one should be used for every invocation of the callback; I needed a second object which would be cheap to create in order to bind the callback with a parameter array for invocation. But, in some scenarios, the invoker would already have a the parameter array for other reasons. For these two reasons, the parameter array does not belong in the Callback object. Also the choice of invocation (passing the parameters as an array or as individual objects) belongs in the hands of the API using the callback enabling it to use whichever invocation is best suited to its inner workings.

The WithParms nested class, then, is optional and serves two purposes, it contains the parameter object array needed for the callback invocations, and it provides 10 overloaded invoke() methods (with from 1 to 10 parameters) which load the parameter array and then invoke the callback target.

What follows is an example using a callback to process the files in a directory tree. This is an initial validation pass which just counts the files to process and ensure none exceed a predetermined maximum size. In this case we just create the callback inline with the API invocation. However, we reflect the target method out as a static value so that the reflection is not done every time.

static private final Method             COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);

...

IoUtil.processDirectory(root,new Callback(this,COUNT),selector);

...

private void callback_count(File dir, File fil) {
    if(fil!=null) {                                                                             // file is null for processing a directory
        fileTotal++;
        if(fil.length()>fileSizeLimit) {
            throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
            }
        }
    progress("Counting",dir,fileTotal);
    }

IoUtil.processDirectory():

/**
 * Process a directory using callbacks.  To interrupt, the callback must throw an (unchecked) exception.
 * Subdirectories are processed only if the selector is null or selects the directories, and are done
 * after the files in any given directory.  When the callback is invoked for a directory, the file
 * argument is null;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);</pre>
 * <p>
 * @return          The number of files processed.
 */
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
    return _processDirectory(dir,new Callback.WithParms(cbk,2),sel);
    }

static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) {
    int                                 cnt=0;

    if(!dir.isDirectory()) {
        if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; }
        }
    else {
        cbk.invoke(dir,(Object[])null);

        File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
        if(lst!=null) {
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

This example illustrates the beauty of this approach - the application specific logic is abstracted into the callback, and the drudgery of recursively walking a directory tree is tucked nicely away in a completely reusable static utility method. And we don't have to repeatedly pay the price of defining and implementing an interface for every new use. Of course, the argument for an interface is that it is far more explicit about what to implement (it's enforced, not simply documented) - but in practice I have not found it to be a problem to get the callback definition right.

Defining and implementing an interface is not really so bad (unless you're distributing applets, as I am, where avoiding creating extra classes actually matters), but where this really shines is when you have multiple callbacks in a single class. Not only is being forced to push them each into a separate inner class added overhead in the deployed application, but it's downright tedious to program and all that boiler-plate code is really just "noise".

 

-----------------------------------

 

http://www.javacamp.org/javavscsharp/delegate.html

 

Java
 
There is no delegate concept in Java
The right-side C# program may be mimiced 
with reflection technology.
====================================
import java.lang.reflect.*;
import java.io.*;
public class Test
{
    public static void main(String[] args) throws Exception
    {
        String[] list= {"to","be","or","not","to","be"};
        Method m1 = Test.class.getMethod("toConsole", 
                           new Class[] {String.class});
        Display(m1, list);
        Method m2 = Test.class.getMethod("toFile", 
                           new Class[] {String.class});
        Display (m2, list);
    }
    public static void toConsole (String str)
    {
        System.out.print(str+" ");
    }
    public static void toFile (String s)
    {
        File f = new File("delegate.txt");
        try{
            PrintWriter fileOut = 
               new PrintWriter(new FileOutputStream(f));
            fileOut.write(s);
            fileOut.flush();
            fileOut.close();
        }catch(IOException ioe) {}
    }
    public static void display(Method m, String[] list)
    {
        for(int k = 0; k < list.length; k++) {
            try {
                Object[] args = {new String(list[k])};
                m.invoke(null, args);
            }catch(Exception e) {}
        }
    }
}
 

C#
 
Delegates are reference types which allow 
indirect calls to methods. There are single and multicast
delegates.
============================================
 using System;
 using System.IO;
 public class DelegateTest
 {
     public delegate void Print (String s);
     public static void Main()
     {
         Print s = new Print (toConsole);
         Print v = new Print (toFile);
         Display (s);
         Display (v);
     }
     public static void toConsole (String str)
     {
         Console.WriteLine(str);
     }
     public static void toFile (String s)
     {
         File f = new File("delegate.txt");
         StreamWriter fileOut = f.CreateText();
         fileOut.WriteLine(s);
         fileOut.Flush();
         fileOut.Close();
     }
     public static void Display(Print pMethod)
     {
         pMethod("This should be displayed in the console");
     }
 }
 A delegate instance encapsulates one or more methods, 
 each of which is referred to as a callable entity. 
 To add or reduce a list of calls
 by using operators += or -=.
 for example
 Print p = s + v;
 s += v;
 

-------------------------------------------

 

In C# you can define delegates anonymously (even though they are nothing more than syntactic sugar). For example, I can do this:

public string DoSomething(Func<string, string> someDelegate)
{
     // Do something involving someDelegate(string s)
} 

DoSomething(delegate(string s){ return s += "asd"; });
DoSomething(delegate(string s){ return s.Reverse(); });

Is it possible to pass code like this in Java? I'm using the processing framework, which has a quite old version of Java (it doesn't have generics).

 

Pre Java 8:

The closest Java has to delegates are single method interfaces. You could use an anonymous inner class.

interface StringFunc {
   String func(String s);
}

void doSomething(StringFunc funk) {
   System.out.println(funk.func("whatever"));
}

doSomething(new StringFunc() {
      public String func(String s) {
           return s + "asd";
      }
   });


doSomething(new StringFunc() {
      public String func(String s) {
           return new StringBuffer(s).reverse().toString();
      }
   });

Java 8 and above:

Java 8 adds lambda expressions to the language.

    doSomething((t) -> t + "asd");
    doSomething((t) -> new StringBuilder(t).reverse().toString());

---------------------------------------------------

 

Your example would look like this in Java, using anomymous inner classes:

interface Func {
    String execute(String s);
}

public String doSomething(Func someDelegate) {
    // Do something involving someDelegate.execute(String s)
}

doSomething(new Func() { public String execute(String s) { return s + "asd"; } });
doSomething(new Func() { public String execute(String s) { return new StringBuilder(s).reverse().toString(); } } });

 

---------------------------------------------------------

 

 

 

----------------------------------------------------------

 

 

 

----------------------------------------------------------

 

 

---------------------------------------------

 

https://github.com/tim-group/karg/blob/master/src/main/java/com/timgroup/karg/reflection/Delegator.java

https://github.com/tim-group/karg/blob/master/src/test/java/com/timgroup/karg/reflection/DelegatorTest.java

 

------------------------------------------

 

Java Proxy class

http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html

 

-------------------------------------------

 

State of the Lambda

http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

 

-----------------------------------------------

 

 

 

 

저작자 표시 비영리 변경 금지
신고

'개발/활용정보' 카테고리의 다른 글

delegate  (0) 2017.07.07
Oauth  (0) 2013.07.18
UML 기본편  (0) 2011.12.05
ClearCase  (0) 2011.08.11
blotware, crapware  (0) 2011.05.27
구글맵 api 를 이용한 map service  (0) 2011.05.20
Trackback 0 And Comment 0

정규식

|

http://docs.oracle.com/javase/tutorial/essential/regex/

 

 

대충 구조는 알았고 그럼 패턴을 만드는 방법을 알아보겠습니다.


10
Pattern p = Pattern.compile("(^[0-9]*$)");
cs


아까 예제1) 에서 사용한 Pattern 을 보면  "(^[0-9]*$)" 이 부분이 바로 패턴 부분입니다.


각각 의미를 알아보면..


표현식

 설명 

 ^

 문자열의 시작

 문자열의 종료

 .

 임의의 한 문자 (문자의 종류 가리지 않음)

 단, \ 는 넣을 수 없음

 *

 앞 문자가 없을 수도 무한정 많을 수도 있음

 앞 문자가 하나 이상

 앞 문자가 없거나 하나있음

 []

 문자의 집합이나 범위를 나타내며 두 문자 사이는 - 기호로 범위를 나타낸다. []내에서 ^가 선행하여 존재하면 not 을 나타낸다.

 {}

 횟수 또는 범위를 나타낸다.

 ()

 소괄호 안의 문자를 하나의 문자로 인식 

 |

 패턴 안에서 or 연산을 수행할 때 사용

 \s

 공백 문자

 \S

 공백 문자가 아닌 나머지 문자

 \w

 알파벳이나 숫자

\W 

 알파벳이나 숫자를 제외한 문자

\d 

 숫자 [0-9]와 동일

\D 

 숫자를 제외한 모든 문자

 정규표현식 역슬래시(\)는 확장 문자
 역슬래시 다음에 일반 문자가 오면 특수문자로 취급하고 역슬래시 다음에 특수문자가 오면 그 문자 자체를 의미

(?i) 

 앞 부분에 (?i) 라는 옵션을 넣어주면 대소문자를 구분하지 않음

▲ 출처 : http://lng1982.tistory.com/141

 

예제의 ^[0-9]*$ 를 분석해보면 


^ 으로 우선 패턴의 시작을 알립니다.

[0-9] 괄호사이에 두 숫자를 넣어 범위를 지정해줄 수 있습니다.

* 를 넣으면 글자 수를 상관하지 않고 검사합니다.

$ 으로 패턴의 종료를 알립니다.


즉 , 0부터 9 까지의 수를 글자 수 제한을 하지 않고 검사하는 패턴입니다.


[자주 쓰이는 패턴]


1) 숫자만 : ^[0-9]*$

2) 영문자만 : ^[a-zA-Z]*$

3) 한글만 : ^[가-힣]*$

4) 영어 & 숫자만 : ^[a-zA-Z0-9]*$

5) E-Mail : ^[a-zA-Z0-9]+@[a-zA-Z0-9]+$

6) 휴대폰 : ^01(?:0|1|[6-9]) - (?:\d{3}|\d{4}) - \d{4}$

7) 일반전화 : ^\d{2.3} - \d{3,4} - \d{4}$

8) 주민등록번호 : \d{6} \- [1-4]\d{6}

9) IP 주소 : ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3})


^[a-zA-Z]*$


a-z 까지 그리고 A-Z 까지 즉, 알파벳은 모두 허용.

* 글자 수 상관하지 않음


-> 알파벳이기만 하면 패턴에 맞음

 


^\\S+.(?i)(txt|pdf|hwp|xls)$


^ : 시작

\ : \ 가 왔기 때문에 다음에 올 문자는 특수문자로 취급하고 , \다음 특수문자고 오면 그 자체로 취급.

\S : 공백 아닌 문자

+. : .이 반드시 한개는 와야한다.

(?i) : 대소문자 구별하지 않음.

(txt|pdf|hwp|xls) : txt 혹은 pdf 혹은 hwp 혹은 xls 만 허용. | 을 이용한 or 연산!

$ : 끝


-> 공백아닌 문자와 .이 반드시 와야하고 뒤에는 txt, pdf, hwp, xls 만 허용.

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

 

.
하나의 문자를 의미합니다.
즉, ab. 은 abc나 abd가 될 수 있습니다.

?
해당 패턴의 앞에 문자가(1개)없거나 혹은 한개가 존재하는 경우를 의미합니다. 즉, a?c는 abc도 될 수 있고, bc도 될 수 있습니다.

*
위와 거의 동일하나 하나 이상의 문자를 의미하게 됩니다.
즉, ab*는 aaa도 ab도 될 수 있다는 의미입니다.

^
문자열의 시작을 의미합니다.
즉, ^a는 a로 시작되는 문자를 검색하게 됩니다.

+
앞의 문자가 하나이상의 반복을 의미합니다.
즉, ab+는 abbbb가 될 수도 있다는 의미입니다.

[^]
괄호안의 형식을 제외한다는 의미입니다.
즉, [^a]bc는 a를 제외한 bc, bcd등을 나타냅니다.

 
$
문자열의 끝을 의미합니다.
즉, ab$가 되는 경우 반드시 b로 끝나는 문자를 나타냅니다.

[ ]

괄호안의 형식이 일치하는 경우를 나타냅니다.
즉, [ab]는 a,b,ab모두가 일치하게 됩니다.

( )
()안의 내용을 하나의 묶음으로 사용합니다.
즉, (ab)+는 위 +에 따라 ababab등을 나타냅니다.

{ }
안의 숫자에 따른 반복개수를 의미합니다.
즉, ab{2}는 abab를 나타냅니다.

|
or연산자와 동일합니다.
즉, a|b|c 는 a,b,c,abc모두가 될 수 있습니다.

4. 정규식 패턴예제

위의 표현방법의 조합을 통해서 우리는 다양하고 편리한 코드를 작성할 수 있습니다. 

E-mail 주소를 찾아내는 패턴
^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@ [A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$

 
이미지파일을 찾아내는 패턴
([^\s]+(\.(?i)(jpg|png|gif|bmp))$)

IP주소를 찾아내는 패턴
^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.
([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$

 
HTML 태그 중 링크를 찾아내는 패턴
(?i)<a([^>]+)>(.+?)</a>\s*(?i)href\s*=\s*(\"([^"]*\")|'[^']*'|([^'">\s]+));
 

 

자바 정규식 표현 방법


 java.util.regex 패키지에 있는 Match 클래스와 Pattern 클래스를 사용하여 문자열을 정규표현식으로 검증할 수 있다.

boolean bln = Pattern.matches("^[a-zA-Z0-9]*$", this.input);


정규표현식은 다음과 같은 문법으로 되어 있다.

^ : 문자열의 시작을 나타냄.

$ : 문자열의 종료를 나타냄.

. : 임의의 한 문자를 나타냄. (문자의 종류는 가리지 않는다)

| : or를 나타냄.


? : 앞 문자가 없거나 하나있음을 나타냄.

+ : 앞 문자가 하나 이상임을 나타냄.

* : 앞 문자가 없을 수도 무한정 많을 수도 있음을 나타냄.


 만약, .* 으로 정규식이 시작한다면 시작하는 문자열과 같은 문자열이 뒤에 없거나 많을 수도 있는 경우에만 일치를 시킨다. 즉, abc 일 경우 시작문자인 a를 기준으로 a가 없을경우와 a가 무한정 많은 경우에도 true를 반환하기 때문에 abc의 경우는 true를 반환한다.


[] : 문자 클래스를 지정할 때 사용한다. 문자의 집합이나 범위를 나타내면 두 문자 사이는 '-' 기호로 범위를 나타낸다. []내에서 ^ 가 선행하여 나타나면 not 를 나타낸다.


{} : 선행문자가 나타나는 횟수 또는 범위를 나타낸다.

a{3} 인 경우 a가 3번 반복된 경우를 말하며, a{3,}이면 a가 3번 이상 반복인 경우를 말한다. 또한 a{3,5}인 경우 a가 3번 이상 5번 이하 반복된 경우를 나타낸다.



\w : 알파벳이나 숫자

\W : 알파벳이나 숫자를 제외한 문자

\d : 숫자 [0-9]와 동일

\D : 숫자를 제외한 모든 문자


기본적인 문자열 검증 정규식

^[0-9]*$  :  숫자만

^[a-zA-Z]*$  :  영문자만

^[가-�R]*$  :  한글만

^[a-zA-Z0-9]*$  :  영어/숫자만


정규식 표현 예제

이메일 : ^[a-zA-Z0-9]+@[a-zA-Z0-9]+$  or  ^[_0-9a-zA-Z-]+@[0-9a-zA-Z-]+(.[_0-9a-zA-Z-]+)*$


휴대폰 :  ^01(?:0|1|[6-9]) - (?:\d{3}|\d{4}) - \d{4}$


일반전화 : ^\d{2,3} - \d{3,4} - \d{4}$


주민등록번호 : \d{6} \- [1-4]\d{6}


IP 주소 : ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3}) \. ([0-9]{1,3})

 

 

저작자 표시 비영리 변경 금지
신고

'개발/활용정보 > Java' 카테고리의 다른 글

ManualResetEvent for Java  (0) 2017.09.07
event handling  (0) 2017.07.10
정규식  (1) 2017.07.05
singleton pattern  (0) 2017.06.29
BundleActivator와 BundleContext  (0) 2014.12.16
guava  (0) 2013.08.08
Trackback 0 And Comment 1
  1. 김군호 2017.10.17 15:04 신고 address edit & del reply

    감사합니다.

singleton pattern

|

https://blog.seotory.com/post/2016/03/java-singleton-pattern

 

 

예전 블로그에서도 singleton 에 대한 글을 쓴적이 있다. 그때는 매우 단순하게 적었으나 이번에는 조금 방대할 것이다. 단일 인스턴스를 다양하게 만들 수 있는 방법을 예제로 통해 한번 알아 보도록 하자.

singleton 이란?

프로그래밍 세계에 OOP 의 개념이 생기면서 객체 자체에 대한 많은 연구와 패턴(pattern)들이 생겨났다. singleton pattern은 인스턴스가 사용될 때에 똑같은 인스턴스를 만들어 내는 것이 아니라, 동일 인스턴스를 사용하게끔 하는 것이 기본 전략이다. 프로그램상에서 동일한 커넥션 객체를 만든다던지, 하나만 사용되야하는 객체를 만들때 매우 유용하다. singleton pattern은 4대 디자인 패턴에 들어갈 정도로 흔히 쓰이는 패턴이다. 물론 core java(java.lang.Runtime, java.awt.Desktop 등등)에서도 singleton pattern이 사용된다.

Eager initialization

아래가 가장 기본적인 singleton pattern이다. 전역 변수로 instance를 만드는데 private static을 이용한다. static이 붙은 클래스변수는 인스턴스화에 상관없이 사용이 가능하게 된다. 하지만 앞의 private 접근제어자로 인해 EagerInitialization.instance로의 접근은 불가능하다. 이런 상태에서 생성자를 private로 명시한다. 생성자를 private로 붙이게되면, new 키워드를 사용할 수 없게된다. 즉 다른 클래스에서 EagerInitialization instance = new EagerInitialization(); 이런 방법을 통한 인스턴스 생성은 불가능해진다. 결국 외부 클래스가 EagerInitialization 클래스의 인스턴스를 가질 수 있는 방법은 11번째 라인에 있는 getInstance() method를 사용하는 수 밖에 없다.

public class EagerInitialization {
	// private static 로 선언.
	private static EagerInitialization instance = new EagerInitialization();
	// 생성자
	private EagerInitialization () {
		System.out.println( "call EagerInitialization constructor." );
	}
	// 조회 method
	public static EagerInitialization getInstance () {
		return instance;
	}
	
	public void print () {
		System.out.println("It's print() method in EagerInitialization instance.");
		System.out.println("instance hashCode > " + instance.hashCode());
	}
}

위의 단순한 singleton pattern은 리소스가 작은 프로그램일때엔 고도화 대상이 아니다. 하지만 프로그램의 크기가 커져서 수 많은 클래스에서 위와 같은 singleton pattern을 사용한다고 가정해보자. 3번째 라인의 new EagerInitialization();으로 인해 클래스가 load 되는 시점에 인스턴스를 생성시키는데 이마저도 부담스러울 수가 있다. 또한 이 소스는 EagerInitialization 클래스가 인스턴스화 되는 시점에 어떠한 에러처리도 할 수가 없다.

static block initialization

public class StaticBlockInitalization {
	private static StaticBlockInitalization instance;
	private StaticBlockInitalization () {}
	
	static {
		try {
			System.out.println("instance create..");
			instance = new StaticBlockInitalization();
		} catch (Exception e) {
			throw new RuntimeException("Exception creating StaticBlockInitalization instance.");
		}
	}
	
	public static StaticBlockInitalization getInstance () {
		return instance;
	}
	
	public void print () {
		System.out.println("It's print() method in StaticBlockInitalization instance.");
		System.out.println("instance hashCode > " + instance.hashCode());
	}
	
}

static 초기화블럭을 이용하면 클래스가 로딩 될 때 최초 한번 실행하게 된다. 특히나 초기화블럭을 이용하면 logic을 담을 수 있기 때문에 복잡한 초기변수 셋팅이나 위와 같이 에러처리를 위한 구문을 담을 수 있다. 첫 번째 패턴보다 좋아보이지만 인스턴스가 사용되는 시점에 생성되는 것은 아니다.

lazy initialization

이제 클래스 인스턴스가 사용되는 시점에 인스턴스를 만드는 singleton pattern을 배워보도록 하자. 아래 소스의 lazy initialization pattern은 필요할때 인스턴스를 생성시키는 것이 핵심이다.

public class LazyInitialization {
	
	private static LazyInitialization instance;
	private LazyInitialization () {}
	
	public static LazyInitialization getInstance () {
		if ( instance == null )
			instance = new LazyInitialization();
		return instance;
	}
	
	public void print () {
		System.out.println("It's print() method in LazyInitialization instance.");
		System.out.println("instance hashCode > " + instance.hashCode());
	}
}

new LazyInitialization(); 가 어디에 선언되었는지 주목해보자. getInstance() method 안에서 사용되었다. if문을 이용해 instance가 null 인 경우에만 new를 사용해 객체를 생성하였다. 최초 사용시점에만 인스턴스화 시키기 때문에 프로그램이 메모리에 적재되는 시점에 부담이 많이 줄게된다. 하지만 여전히 문제는 남아있다. 만약 프로그램이 muilti thread 방식이라면 위와 같은 singleton pattern은 안전하지 않다. 동일 시점에 getInstance() method를 호출하면 인스턴스가 두번 생길 위험이 있다.

thread safe initalization

위에서 문제가 되었던 muilit thread문제를 해결하기 위해 synchronized(동기화)를 사용하여 singleton pattern을 구현한다. 여러 thread들이 동시에 접근해서 인스턴스를 생성시키는 위험은 없어졌다. 하지만 수 많은 thread 들이 getInstance() method 를 호출하게 되면 높은 cost 비용으로 인해 프로그램 전반에 성능저하가 일어난다.

public class ThreadSafeInitalization {
	
	private static ThreadSafeInitalization instance;
	private ThreadSafeInitalization () {}
	
	public static synchronized ThreadSafeInitalization getInstance () {
		if (instance == null)
			instance = new ThreadSafeInitalization();
		return instance;
	}
	
	public void print () {
		System.out.println("It's print() method in ThreadSafeInitalization instance.");
		System.out.println("instance hashCode > " + instance.hashCode());
	}
	
}

initialization on demand holder idiom

미국 메릴랜드 대학의 컴퓨터 과학 연구원인 Bill pugh 가 기존의 java singleton pattern이 가지고 있는 문제들을 해결 하기 위해 새로운 singleton pattern을 제시하였다. Initialization on demand holder idiom기법이다. 이것은 jvm 의 class loader의 매커니즘과 class의 load 시점을 이용하여 내부 class를 생성시킴으로 thread 간의 동기화 문제를 해결한다.

public class InitializationOnDemandHolderIdiom {
	
	private InitializationOnDemandHolderIdiom () {}
	private static class Singleton {
		private static final InitializationOnDemandHolderIdiom instance = new InitializationOnDemandHolderIdiom();
	}
	
	public static InitializationOnDemandHolderIdiom getInstance () {
		System.out.println("create instance");
		return Singleton.instance;
	}
}

initialization on demand holder idiom 역시 lazy initialization이 가능하며 모든 java 버젼과, jvm에서 사용이 가능하다. 현재 java 에서 singleton 을 생성시킨다고 하면 거의 위의 방법을 사용한다고 보면 된다.

enum initialization

Joshua Bloch가 작성한 effective java 책에서 enum singleton 방법이 소개 되었다.

public enum EnumInitialization {
	INSTANCE;
	static String test = "";
	public static EnumInitialization getInstance() {
		test = "test";
		return INSTANCE;
	}
}

enum 이 singleton pattern 으로 사용될 수 있는 이유는 아래와 같다.

  • INSTANCE 가 생성될 때, multi thread 로 부터 안전하다. (추가된 methed 들은 safed 하지 않을 수도 있다.)
  • 단 한번의 인스턴스 생성을 보장한다.
  • 사용이 간편하다.
  • enum value는 자바 프로그램 전역에서 접근이 가능하다.

using reflection to destroy singleton

위에서 여러 방법으로 singleton을 만들어 보았으니 이번엔 java의 reflection을 이용하여 singleton을 깨뜨려 보는법도 배워보자. 누군가 작성한 코드를 원본 수정없이 작업해야 할때 이용될 수 있을 것이다.

public class UsingReflectionToDestroySingleton {
	
	public static void main (String[] args) {
		EagerInitialization instance = EagerInitialization.getInstance();
		EagerInitialization instance2 = null;
		
		try {
			Constructor[] constructors = EagerInitialization.class.getDeclaredConstructors();
			for ( Constructor constructor : constructors ) {
				constructor.setAccessible(true);
				instance2 = (EagerInitialization)constructor.newInstance();
			}
		} catch (Exception e) {
			
		}
		
		System.out.println(instance.hashCode());
		System.out.println(instance2.hashCode());
		
	}
}

위의 코드를 실행해보면 아래 System.out.println();의 두 라인에서 찍히는 hachCode()값이 다른 것을 확인 할 수 있다. java의 reflection은 매우 강력하다. 설령 class 의 생성자가 private 일지라도 강제로 가져와서 새로운 인스턴스 생성이 가능하다. 결국 singleton pattern을 깨뜨리는 것이다. 이 외에도 reflection을 여러곳에서 사용할 수 있으니 알아두는 것이 좋다.

 

 

 

저작자 표시 비영리 변경 금지
신고

'개발/활용정보 > Java' 카테고리의 다른 글

event handling  (0) 2017.07.10
정규식  (1) 2017.07.05
singleton pattern  (0) 2017.06.29
BundleActivator와 BundleContext  (0) 2014.12.16
guava  (0) 2013.08.08
osgi 공부 ^^;;;  (0) 2013.07.10
Trackback 0 And Comment 0
prev | 1 | 2 | 3 | 4 | 5 | ··· | 83 | next