Refactoring 이란?
소프트웨어를 보다 쉽게 이해할 수 있고, 적은 비용으로 수정할 수 있도록 겉으로 보이는 동작의 변화 없이 내부 구조를 변경하는 것

핵심 정리
  • 새로운 기능을 추가해야 하는데 프로그램의 코드가 새로운 기능을 추가하기 쉽도록 구조화되어있지 않은 경우에는 먼저 리팩토링을 해서 프로그램에 기능을 추가하기 쉽게 하고, 그 다음에 기능을 추가한다.
  • 리팩토링을 시작하기 전에 견고한 테스트 세트를 가지고 있는지 확인하라. 이 테스트는 자체 검사여야 한다.
  • 리팩토링은 작은 단계로 나누어 프로그램을 변경한다. 실수를 하게 되더라도 쉽게 버그를 찾을 수 있다.
  • 컴퓨터가 이해할 수 있는 코드는 어느 바보나 다 짤 수있다. 좋은 프로그래머는 사람이 이해할 수 있는 코드를 짠다.
  • 완전하지 않은 인터페이스를 공표하지 마라. 매끄러운 리팩토링을 위해 코드 소유권 정책을 수정하라.

왜 리팩토링을 해야 하는가?

  • 리팩토링은 소프트웨어의 디자인을 개선시킨다.
  • 리팩토링은 소프트웨어를 더 이해하기 쉽게 만든다.
  • 리팩토링은 버그를 찾도록 도와준다.
  • 리팩토링은 프로그램을 빨리 작성하도록 도와준다.


언제 리팩토링을 해야 하는가?

  • 별도의 시간을 내는것이 아니라, 틈틈히 계속 해야 하는 것이다.
  • 어떤 것을 할 때 비슷한 어떤것을 하게 되면 리팩토링을 한다.(책에서는 3번째 부터 하라고 나와있지만 바로 한다.)
  • 기능을 추가할 때 리팩토링을 해라.
  • 버그를 수정해야 할 때 리팩토링을 하라.
  • 코드 검토(code review)를 할 때 리팩토링을 하라.
    • 읽기 어려운 프로그램은 수정하기 어렵다.
    • 중복된 로직을 가지고 있는 프로그램 수정하기 어렵다.
    • 실행중인 코드를 변경해야 하는 특별한 동작을 요구하는 프로그램은 수정하기 어렵다.
    • 복잡한 조건문이 포함된 프로그램은 수정하기 어렵다.

언제 리팩토링을 하지 말아야 하는가?

  • 코드를 처음부터 다시 작성해야 할때
  • 마감일에 가까울 때


리팩토링과 디자인
리팩토링이 사전 디자인의 역할을 바꿀수 있기 때문에 간단한 솔루션을 만든 다음에 코딩을 하고 리팩토링을 한다.
※ 시스템이 어떻게 돌아가는지 정확하게 알고 있다 하더라도, 추측만 하지 말고 실제로 퍼포먼스를 측정해보라. 무엇인가 배울 것이고, 십중팔구는 추측이 틀렸을 것이다.

리팩토링과 퍼포먼스
리팩토링은 확실히 소프트웨어를 더 느리게 할 것이지만, 반면에 소프트웨어에 대한 퍼포먼스 튜닝을 더 쉽게 할 수 있도록 만든다.

 

Posted by outliers
,

'Programs > eclipse tip' 카테고리의 다른 글

이클립스 단축키  (4) 2011.09.20
Posted by outliers
,


AccountManager am = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
Account[] accounts = am.getAccounts();
for (int i = 0; i < accounts.length; i++)
{
        Account account = accounts[i];
        Log.i("test", account.name);
}

Posted by outliers
,
State Pattern

정의
객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.


상태를 별도의 클래스로 캡슐화한 다음 현재 상태를 나타내는 객체에게 행동을 위임하기 때문에, 내부 상태가 바뀜에 따라서 행동이 달라지게 된다는 것을 알 수 있습니다.


객체지향 디자인 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
- 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.


핵심정리
- 스테이트 패턴을 이용하면 내부 상태를 바탕으로 여러 가지 서로 다른 행동을 사용할 수 있습니다.
- 스테이트 패턴을 사용하면 프로시저형 상태 기계를 쓸 때와는 달리 각 상태를 클래스를 이용하여 표현하게 됩니다.
- Context 객체에서는 현재 상태에게 행동을 위임합니다.
- 각 상태를 클래스로 캡슐화함으로써 나중에 변경시켜야 하는 내용을 국지화시킬 수 있습니다.
- 스테이트 패턴과 스트래티지 패턴의 클래스 다이어그램은 똑같지만 그 용도는 서로 다릅니다.
- 스트래티지 패턴에서는 일반적으로 행동 또는 알고리즘을 Context 클래스를 만들 때 설정합니다.
- 스테이트 패턴을 이용하면 Context의 내부 상태가 바뀜에 따라 알아서 행동을 바꿀수 있도록 할 수 있습니다.
- 상태 전환은 State 클래스에 의해서 제어할 수도 있고, Context 클래스에 의해서 제어할 수도 있습니다.
- 스테이트 패턴을 이용하면 보통 디자인에 필요한 클래스의 개수가 늘어납니다.
- State 클래스를 여러 Context 객체의 인스턴스에서 공유하도록 디자인할 수도 있습니다.
Posted by outliers
,
Iterator Pattern

정의
컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해 주는 방법을 제공해 줍니다.

장점
집합체 내에서 어떤 식으로 일이 처리되는지에 대해서 전혀 모르는 상태에서 그 안에 들어있는 모든 항목들에 대해서 반복작업을 수행할 수 있습니다.
컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식이 통일되어 있으면 어떤 종류의 집합체에 대해서도 사용할 수 있는 다형적인 코드를 만들 수 있습니다.
이터레이터 패턴을 사용하면 모든 항목에 일일이 접근하는 작업을 컬렉션 객체가 아닌 반복자 객에에서 맡게 된다는 점입니다. 이렇게 하면 집합체의 인터페이스 및 구현이 간단해 질 뿐 아니라 집합체에서는 반복작업에서 손을 떼고 원래 자신이 할 일(객체 컬렉션 관리)에만 전념할 수 있습니다.

객체지향 디자인 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
- 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.


Composite Pattern

정의
객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있습니다. 이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체(composite)를 똑같은 방법으로 다룰 수 있습니다.

특징
컴포지트 패턴에서는 단일 역할 원칙을 깨면서 대신에 투명성을 확보하기 위한 패턴이라고 할 수 있습니다.
어떤 원소가 복합 객체인지 잎 노드인지가 클라이언트 입장에서는 투명하게 느껴지는 거죠. 원칙이 우리가 생각하고 있는 디자인에 어떤 영향을 끼칠지를 생각해 봐야 합니다.
가장 큰 특징은 클라이언트를 단순화 시킬 수 있다는 점입니다. 올바른 객체에 올바른 메소드를 호출하고 있는지 확인하기 위해 if문을 쓰지 않아도 됩니다. 그리고 메소드 하나만 호출하면 전체 구조에 대해서 반복해서 작업을 처리할 수 있는 경우도 자주 있습니다.
객체들을 모아서 관리할 때 매우 유용할 것입니다.

핵심 정리
- 반복자를 이용하면 내부 구조를 드러내지 않으면서도 클라이언트로부터 컬렉션 안에 들어있는 모든 원소들에 접근하도록 할 수 있습니다.
- 이터레이터 패턴을 이용하면 집합체에 대한 반복작업을 별도의 객체로 캡슐화할 수 있습니다.
- 이터레이터 패턴을 이용하면 컬렉션에 있는 모든 데이터에 대해서 반복작업을 하는 역할을 컬렉션에서 분리시킬 수 있습니다.
- 이터레이터 패턴을 쓰면 다양한 집합체에 들어있는 객체에 대한 반복작업들에 대해 똑같은 인터페이스를 적용할 수 있기 때문에, 집합체에 있는 객체를 활용하는 코드를 만들 때 다형성을 활용할 수 있습니다.
- 한 클래스에는 될 수 있으면 한 가지 역할만 부여하는것이 좋습니다.
- 컴포지트 패턴에서는 개별 객체와 복합 객체를 모두 담아둘 수 있는 구조를 제공합니다.
- 컴포지트 패턴을 이용하면 클라이언트에서 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있습니다.
- 복합 구조에 들어있는 것을 구성요소라고 부릅니다. 구성요소에는 복합 객체와 잎 노드가 있습니다.
- 컴포지트 패턴을 적용할 때는 여러가지 장단점을 고려해야 합니다. 상황에 따라 투명성과 안정성 사이에서 적절한 평형점을 찾아야 합니다.
Posted by outliers
,
Template Method Pattern

정의
메소드에서 알고리즘의 골격을 정의합니다. 알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다. 템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있습니다.

간단하게 말하자면 알고리즘의 틀을 만들기 위한 것입니다. 일련의 단계들로 알고리즘을 정의한 메소드 입니다. 여러 단계 가운데 하나 이상이 추상 메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현됩니다.

 
abstract class AbstractClass {
	final void templateMethod() {
		primitiveOperation1();
		primitiveOperation2();
		if (hook()) {
			concreteOperation();
		}
	}
	
	// 알고리즘의 일부를 구상 서브 클래스에서 구현한다.
	abstract void primitiveOperation1();
	abstract void primitiveOperation2();
	
	/**
	 *  기본적으로 아무것도 하지 않는 구상 메소드를 정의할 수도 있습니다.
	 *  이런 메소드는 "후크(hook)"라고 부릅니다. 
	 *  서브클래스에서 오버라이드 할 수도 있지만 반드시 그래야 하는건 아니죠.
	 *  서브 클래스에서 오버라이드를 해서 알고리즘의 일부를 변경 할 수 있습니다.
	 */
	boolean hook() {
		return true;
	}

	void concreteOperation() {
		// TO DO
	}	
}


헐리우드 원칙
먼저 연락하지 마세요. 저희가 연락 드리겠습니다.

헐리우드 원칙을 적용을 활용하면 "의존성 부패(dependency rot)"를 방지할 수 있습니다.
저수준 구성요소에서 시스템에 접속을 할수는 있지만, 언제 어떤 식으로 그 구성요소들을 사용할지는 고수준 구성요소에서 결정하게 됩니다. 즉, 고수준 구성요소에서는 저수준 구성요소에게 "먼저 연락하지 마세요. 제가 먼저 연락 드리겠습니다."라고 얘기를 하는 것과 같죠

Arrays.sort() 도 변형된 템플릿 메소드 패턴 입니다.

객체지향 원칙
- 바뀌는 부분은 캡슐화 한다.
- 상속보다는 구성을 활용한다.
- 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
- 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
- 클래스는 확장에 대해서는 열려있지만 변경에 대해서는 닫혀있어야 한다.(OCP)
- 추상화된 것에 의존하라. 구상 클래스에 의존하지 않도록 한다.
- 친한 친구들하고만 이야기 한다.
- 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.

핵심 정리
- "템플릿 메소드"에서는 알고리즘의 단계들을 정의하는데, 일부 단계는 서브클래스에서 구현하도록 할 수 있습니다.
- 템플릿 메소드 패턴은 코드 재사용에 크게 도움이 됩니다.
- 템플릿 메소드가 들어있는 추상클래스에서는 구상 메소드, 추상 메소드, 후크를 정의할 수 있습니다.
- 추상 메소드는 서브클래스에서 구현합니다.
- 후크(hook)는 추상 클래스에 들어있는, 아무 일도 하지 않거나 기본 행동을 정의하는 메소드로, 서브클래스에서 오버라이드 할 수 있습니다.
- 서브클래스에서 템플릿 메소드에 들어있는 알고리즘을 함부로 바꾸지 못하게 하고 싶다면 템플릿 메소드를 final로 선언하면 됩니다.
- 헐리우드 원칙에 의하면, 저수준 모듈을 언제 어떻게 호출할지는 고수준 모듈에서 결정하는 것이 좋습니다.
- 템플릿 메소드 패턴은 실전에서도 꽤 자주 쓰이지만 반드시 "교과서적인" 방식으로 적용되진 않습니다.
- 스트래티지 패턴과 템플릿 메소드 패턴은 모두 알고리즘을 캡슐화하는 패턴이지만 전자에서는 구성을, 후자에서는 상속을 이용합니다.
Posted by outliers
,

출처 : http://journae.springnote.com/pages/6730933
참조 URL : http://2nd2none.tistory.com/36#tb

주의 :   

          <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
      위와 같은 activity의 경우 

<data android:scheme="myapp"  />

와 같은 설정을 추가해도 scheme을 인식하지 못한다..(내가 테스트한 바로는..)

따라서 android 단말기의 브라우저에서 해당 scheme( "myapp") 을 인식하고 찾아가게 하기 위해서는

이 페이지의 맨 하단의 내용과 같은 설정과 코딩 부분이 들어가면 된다.

 

참조 코드 전문 :

[AndroidManifest.xml]

        <activity android:name=".Activity.XenoboxCustomDataSchemeActivity">
            <intent-filter>  
                <action android:name="android.intent.action.VIEW"/>  
                <category android:name="android.intent.category.DEFAULT"/>  
                <category android:name="android.intent.category.BROWSABLE"/>  
                <data android:scheme="myapp"/>  
            </intent-filter> 
        </activity>

[assets 폴더]
    [test.xml]

        <html>

            <head>
            </head>

            <body>
                <a href="myapp://someaction?var=str&varr=string">Foo</a> 
            </body>
    
        </html>


[XenoboxCodeLabAppCustomScheme extends Activity]

    @Override
    public void onCreate(Bundle savedInstanceState) {
        //.
        mWebView = (WebView) findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.loadUrl("file:///android_asset/test.html");
        mWebView.setWebViewClient(new XenoboxWebViewClient());
    }


    protected class XenoboxWebViewClient extends WebViewClient {

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (url.startsWith("myapp:")) {
                Intent i = new Intent();
                i.setAction(Intent.ACTION_VIEW);
                i.setData(Uri.parse(url));
                startActivity(i);
                return true;
            }
            return false;

        }
    }

[XenoboxCustomDataSchemeActivity extends Activity]

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //.
        Intent intent = getIntent(); 
        if(Intent.ACTION_VIEW.equals(intent.getAction())) { 
            Uri uri = intent.getData(); 
            String var = uri.getQueryParameter("var");        //. "str" is set 
            String varr = uri.getQueryParameter("varr");    //. "string" is set
            
Log.i("xenobox", "var=" + var + ", " + "varr=" + varr);            
        } 
    }

 

 

 

출처 : http://stackoverflow.com/questions/2448213/how-to-implement-my-very-own-uri-schema-on-android/2448531#2448531

 

This is very possible; you define the URI schema in your AndroidManifest.xml, using the <data> element. You setup an intent filter with the <data> element filled out, and you'll be able to create your own schema. (More on intent filters and intent resolution here.)

Here's a short example:

          <activityandroid:name=".MyUriActivity"><intent-filter><actionandroid:name="android.intent.action.VIEW"/><categoryandroid:name="android.intent.category.DEFAULT"/><categoryandroid:name="android.intent.category.BROWSABLE"/><dataandroid:scheme="myapp"android:host="path"/></intent-filter></activity>
          

As per how implicit intents work, you need to define at least one action and one category as well; here I picked VIEW as the action (though it could be anything), and made sure to add the DEFAULT category (as this is required for all implicit intents). Also notice how I added the category BROWSABLE - this is not necessary, but it will allow your URIs to be openable from the browser (a nifty feature).

 

 

출처 ; http://stackoverflow.com/questions/2430045/how-to-register-some-url-namespace-myapp-app-start-for-accessing-your-progra/2430468#2430468

 

 

You need to follow the standard rules for URIs via the W3C and such, which basically means: do not do this.

Android defines a Uri syntax for describing a generic Intent. There are methods on Intent for converting to and from this representation, such as: http://developer.android.com/reference/android/content/Intent.html#toUri(int)

So the way to do this is to use the normal facilities to describe an in your manifest for the kinds of intents you are going to handle with a particular component, especially defining an action name in your own namespace (com.mycompany.myapp.action.DO_SOMETHING or whatever). You can then make an Intent that matches your component, and use Intent.toUri() to get the URI representation of this. This can be placed in your link, and will then when pressed look for something that handles and and thus find your app. Note to be launched from the browser like this, the component's must handle the BROWSABLE category. (You don't need to have this in the Intent you put in the link, the browser will automatically add this in for you.)

Finally, you may want to set the package of the intent to your app with this: http://developer.android.com/reference/android/content/Intent.html#setPackage(java.lang.String)

This is a newer feature in the platform, which allows you to direct link intents to only your app so that other applications can not intercept and handle them.

In summary: read the regular documentation on intents and intent filters (such as the NotePad tutorial, though you won't be using content: URIs here, probably just custom actions) and get your app working that way. Then you can make a browser link to launch your app in the same way, provided your intent-filter handles the BROWSABLE category.

 

 

출처 ; http://stackoverflow.com/questions/2430045/how-to-register-some-url-namespace-myapp-app-start-for-accessing-your-progra/2430468#2430468

 

 

 

First, to be able to start your app from link with custom scheme 'myapp' in browser / mail, set intent filter as follows.

          <intent-filter>   <== AndroidMenifest.xml 의 내용
          
          <actionandroid:name="android.intent.action.VIEW"/>
          
          <categoryandroid:name="android.intent.category.DEFAULT"/>
          
          <categoryandroid:name="android.intent.category.BROWSABLE"/>
          
          <dataandroid:scheme="myapp"/>
          
          </intent-filter>
          
           
          

and to parse queries in your link myapp://someaction/?var=str&varr=string
(the code is over simplified and has no error checking.)

// 소스코드 내용.

          Intent intent = getIntent();// check if this intent is started via custom scheme link
          
          if(Intent.ACTION_VIEW.equals(intent.getAction())){
          
          Uri uri = intent.getData();
          
          // may be some test here with your custom uriString
          
          var= uri.getQueryParameter("var");// "str" is setString
          
          varr = uri.getQueryParameter("varr");// "string" is set
          
          }
          
Posted by outliers
,

어댑터 패턴(Adapter Pattern)

정의

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환합니다. 어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있습니다.

- 이 패턴을 이용하면 호환되지 않는 인터페이스를 사용하는 클라이언트를 그대로 활용할 수 있습니다.
- 어댑티를 새로 바뀐 인터페이스로 감쌀 때는 객체 구성(Composition)을 사용합니다.
- 클라이언트를 특정 구현이 아닌 인터페이스에 연결 시킵니다
.

// Target Interface
public interface Duck {
	public void quack();
	public void fly();
}

public class DuckAdapter implements Turkey {
	Duck duck;	
	public DuckAdapter(Duck duck) {
		this.duck = duck;
	}
	public void gobble() {
		duck.quack();
	}
	public void fly() {
		duck.fly();
	}
}
// Adaptee Interface
public interface Turkey {
	public void gobble();
	public void fly();
}

public class WildTurky implements Turkey {
	public void gobble() {
		System.out.println("Gobble gobble");
	}
	public void fly() {
		System.out.println("I'm flying a short distnace");
	}
}
// Adapter에서는 Target Interface를 구현하고, Adapter는 Adaptee Interface로 구성되어 있습니다.
// 모든 요청은 Adaptee 에게 위임 됩니다. 
public class TurkeyAdapter implements Duck {
	Turkey turkey;
	
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
	public void quack() {
		turkey.gobble();
	}
	public void fly() {
		for (int i = 0; i < 5; i++) {
			turkey.fly();
		}
	}
}
// Client 에서는 target Interface 만 볼 수 있습니다.
public class Main {
	public static void main(String[] args) {
		MallardDuck duck = new MallardDuck();
		
		WildTurky turkey = new WildTurky();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);
		
		System.out.println("The Turkey says.....");
		turkey.gobble();
		turkey.fly();
		
		System.out.println("\nThe Duck says.....");
		testDuck(duck);
		
		System.out.println("\nThe TurkeyAdapter says.....");
		testDuck(turkeyAdapter);
	}
	
	static void testDuck(Duck duck) {
		duck.quack();
		duck.fly();
	}
}


퍼사드 패턴(Facade Pattern)

정의

어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공합니다. 퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있습니다.

public class Facade {
	// 우리가 사용하고자 하는 서브시스템의 모든 구성요소들이 인스턴스 변수 형태로 저장 됩니다.
	DvdPlayer dvd;
	Light light;
	Screen screen;
	
	public Facade(DvdPlayer dvd, Light light, Screen screen) {
		this.dvd = dvd;
		this.light = light;
		this.screen = screen;
	}
	// 하나하나 수동으로 작업 했던 내용들을 하나의 메소드로 순서대로 처리 합니다.
	//  각 작업은 서브시스템에 들어있는 구성요소들에게 위임 됩니다.
	public watchMove(String movie) {
		dvd.on();
		light.on();
		dvd.open();
		dvd.insert();
		screen.on();
		dvd.play();
	}
}


객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)
7. 최소 지식 원칙(Principle of Least Knowledge) - 정말 친한 친구하고만 얘기하라.
- 시스템을 디자인할 때, 어떤 객체든 그 객체와 상호작용을 하는 클래스의 개수에 주의해야 하며, 그런 객체들과 어떤 식으로 상호작용을 하는지에도 주의를 기울여야 한다
- 최소 지식 원칙 단점 : 이 원칙을 적용하다 보면 다른 구성요소에 대한 메소드 호출을 처리하기 위해 "래퍼" 클래스를 더 만들어야 할 수도 있습니다. 그러다 보면 시스템이 더 복잡해지고, 개발 시간도 늘어나고, 성능이 떨어질 수도 있습니다.

7번 원칙에 대한 가이드 라인을 제시 합니다. 어떤 메소드에서든지 다음 네 종류의 객체의 메소드만을 호출 하면 됩니다.
1. 객체 자체
2. 메소드에 매개변수로 전달된 객체
3. 그 메소드에서 생성하거나 인스턴스를 만든 객체
4. 그 객체에 속하는 구성요소

예제)
- 원칙을 따르지 않은 경우

// station으로부터 thermometer라는 객체를 받은 다음, 그객체의 
// getTemperature() 메소드를 직접 호출 합니다.
public float getTemp() {
	Thermometer thermometer = station.getThermometer();
	return thermometer.getTemperature();
}


- 원칙을 따르는 경우
// 최소 지식 원칙을 적용하여 Station 클래스에 thermometer에 요청을 해 주는
// 메소드를 추가했습니다. 이렇게 하면 의존해야 하는 클래스의 개수를 줄일 수 있죠.
public float getTemp() {
	return station.getTemperature();
}


최소 지식 원칙을 따르면서 메소드를 호출하는 방법 예제
 
public class Car {
	// 클래스의 구성요서. 이 구성요소의 메소드는 호출해도 되죠.
	Engine engine;
	// 기타 인스턴스 변수
	
	public Car() {
		// 엔진 초기화 등을 처리
	}
	
	public void start(Key key) {
		// 새로운 객체를 생성 합니다. 이 객체의 메소드는 호출해도 됩니다.
		Doors doors = new Doors();
		// 매개변수로 전달된 객체의 메소드는 호출해도 됩니다.
		boolean authorized = key.turns();
		
		if (authorized) {
			// 이 객체의 구성요소의 메소드는 호출해도 되죠?
			engine.start();	
			// 객체 내에 있는 메소드는 호출해도 됩니다.
			updateDashboardDisplay();
			// 직접 생성하거나 인스턴스를 만든 객체의 메소드는 호출해도 됩니다.
			doors.lock();	
		}
		
	}
	
	public void updateDashboardDisplay() {
		// 디스플레이 갱신
	}
}


어댑터 패턴과 퍼사드 패턴의 차이점
어댑터 패턴은 인터페이스를 변경해서 클라이언트에서 필요로 하는 인터페이스로 적응시키기 위한 용도
퍼사드 패턴은 어떤 서브시스템에 대한 간단한 인터페이스를 제공하기 위한 용도
퍼사드는 인터페이스를 단순화 시키기 위한 용도로 쓰이는 반면, 어댑터는 인터페이스를 다른 인터페이스로 변환하기 위한 용도로 쓰입니다.

핵심 정리
- 기존 클래스를 사용하려고 하는데 인터페이스가 맞지 않으면 어댑터를 쓰면 됩니다.
- 큰 인터페이스, 또는 여러 인터페이스를 단순화시키거나 통합 시켜야 되는 경우에는 퍼사드를 쓰면 됩니다.
- 어댑터는 인터페이스를 클라이언트에서 원하는 인터페이스로 바꿔주는 역할을 합니다.
- 퍼사드는 클라이언트를 복잡한 서브시스템과 분리시켜주는 역할을 합니다.
- 어댑터를 구현할 때는 타겟 인터페이스의 크기와 구조에 따라 코딩해야 할 분량이 결정됩니다.
- 퍼사드 패턴에서는 서브시스템을 가지고 퍼사드를 만들고, 실제 작업은 서브클래스에 맡깁니다.
- 어댑터 패턴에는 객체 어댑터 패턴과 클래스 어댑터 패턴이 있다. 클래스 어댑터를 쓸려면 다중 상속 기능이 필요 합니다.
- 한 서브시트템에 퍼사드를 여러개 만들어도 됩니다.
- 어댑터는 객체를 감싸서 인터페이스를 바꾸기 위한 용도로, 데코레이터는 객체를 감싸서 새로운 행동을 추가하기 위한 용도로, 퍼사드는 일련의 객체들을 감싸서 단순화 시키기 위한 용도로 쓰입니다.
Posted by outliers
,

정의
요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있습니다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업취소 기능도 지원 가능 합니다.
어떤 작업을 요청한 쪽하고 그 작업을 처리한 쪽을 분리시킬 수 있다.

브레인 파워
커맨드 패턴 디자인을 사용했을 때 작업을 요구한 인보커와 작업을 처리하는 리시버가 어떤 식으로 분리되는 걸까요?
Invoker ---> Interface(Command) ---> Receiver
구성(Composition) 을 통해서 분리되어 있다.??

Null Object
클라이언트 쪽에서 null을 처리하지 않아도 되도록 하고 싶을 때 널 객체를 활용

Command noCommand = new NoCommand();
onCommand = noCommand
// 널 객체를 사용하면 if 문을 생략하고 사용할 수 있음.
if (onCommand != null)
    onCommand.execute();

다른 커맨드를 실행시킬수 있는 새로운 종류의 커맨드를 만들어서 여러 가지 커맨드를 한꺼번에 실행시킬 수 있는 생각
매크로커맨드
public class MacroCommand implements Command {
    Command[] command;
    public MacroCommand(Command[] command) {
        this.command = command;
    }
    public void execute() {
        for (int i = 0; i < command.length; i++) {
            command[i].execute();
        }
    }
    public void undo() {
        for (int i = 0; i < command.length; i++) {
            command[i].undo();
        }
    }
}


객체지향 원칙(디자인 원칙)
1. 애플리케이션에서 바뀌는 부분을 찾아내서 바뀌지 않는 부분으로부터 분리 시켜 캡슐화 한다.
2. 상속보다는 구성을 활용한다.
3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.
4. 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
5. 클래스는 확장에 대해서는 열려 있지만 변경에 대해서는 닫혀 있어야 한다.(OCP : Open-Closed Principle)
6. 추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다. (의존성 뒤집기 윈칙:Dependency Inversion Principle)

핵심 정리
- 커맨드 패턴을 이용하면 요청을 하는 객체와 그 요청을 수행하는 객체를 분리시킬 수 있습니다.
- 이렇게 분리시키는 과정의 중심에는 커맨드 객체가 있으며, 이 객체가 행동이 들어있는 리시버를 캡슐화 합니다.
- 인보커에서는 요청을 할 때는 커맨드 객체의 execute()메소드를 호출하면 됩니다. execute() 메소드에서는 리시버에 있는 행동을 호출합니다.
- 인보커는 커맨드를 통해서 매개변수화될 수 있습니다.
- execute() 메소드가 마지막으로 호출되기 전의 기존 상태로 되돌리기 위한 작업취소 메소드를 구현하면 커맨드 패턴을 통해서 작업취소 기능을 지원할 수도 있습니다.
- 매크로 커맨드는 커맨드를 확장해서 여러 개의 커맨드를 한꺼번에 호출할 수 있게 해주는 간단한 방법입니다. 매크로 커맨드에서도 어렵지 않게 작업취소 기능을 지원할 수 있습니다.
- 프로그래밍을 하다 보면 요청자체를 리시버한테 넘기지 않고 자기가 처리하는 "스마트" 커맨드 객체를 사용하는 경우도 종종 있습니다.
- 커맨드 패턴을 활용하여 로그및 트랜잭션 시스템을 구현하는 것도 가능합니다.

Posted by outliers
,

Singleton Pattern
정의
해당 클래스의 인스턴스가 하나만 만들어 지고, 어디서든 그 인스턴스에 접근할 수 있도록 하기 위한 패턴입니다.

싱글톤 패턴 예
public class Singleton {
    private static Singleton instance;

    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

위와 같이 사용 할 경우 멀티 스레드 환경에서 여러개의 인스턴스가 생성 될 수 있습니다. 그래서 getInstance() 에 synchronized 를 사용해서 스레드 환경에서 발생되는 문제를 해결 할 수 있습니다. 문제는 해결되긴 하지만 동기화를 하면 속도가 느려지는 문제가 발생 합니다. 동기화가 꼭 필요한 시점은 인스턴스를 만드는 그 시점 한 번 뿐이지만 아래와 같이 사용을 하면 불필요한 오버헤드만 증가됩니다.
public class Singleton {
    private static Singleton instance;	
    private Singleton() {}	
    /**
     * getInstance()에 synchronized 키워드만 추가하면 한 스레드가 
     * 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 합니다.
     * 즉, 두 스레드가 이메소드를 동시에 실행시킬 수 없습니다.
     */
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

싱글톤을 사용하기 위한 방법
1. getInstance() 의 속도가 그리 중요하지 않으면 그냥 동기화를 사용한다.
2. 인스턴스를 필요할 때 생성하지 말고 처음부터 만들어 버린다.

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

3. DCL(Double-Checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.
public class Singleton {
    // 1.5 이전의 버전에서는 volatile 키워드를 사용 할 수 없습니다.
    private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            // 이 방법을 사용 하면 처음에 한번만 동기화가 된다.
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Posted by outliers
,