이번 강좌에서는 GoF의 분류에 따라 생성(Creational) 디자인 패턴 중 하나인 Builder 패턴을 소개합니다.


Builder패턴은 여러 개의 복잡한 객체를 모아 합성을 하는 공정이 있고, 복합 객체의 생성알고리즘이 요소 객체들 각각의 생성방법과 조립방법에 독립적일 때 쓰입니다.

도대체 이게 무슨 소릴까요? 좀 더 쉽게 이야기 하면 객체들을 조립하는 인터페이스만 외부에 공개하고 내부에 실제 생성되는 객체들을 숨기자~ 하는 것이지요.

예제를 한 번 볼까요? 요구사항이 다음과 같이 주어졌습니다.
 

어떤 내용을 포함하는 문서를 Text와 HTML 포맷으로 작성하도록 하여, HTML을 지원하지 않는 디바이스에서도 내용을 볼 수 있도록 한다.


즉, 다음과 같은 출력결과를 가지도록 하는 게 목표입니다.


<Text로 출력 결과>

==============================
『Greeting』

■아침과 낮에

●좋은 아침입니다.
●안녕하세요

■밤에

●안녕하세요
●안녕히 주무세요
●안녕히 계세요

==============================

<HTML 파일로 저장 - 브라우저로 볼 경우>

Greeting

아침과 낮에

  • 좋은 아침입니다.
  • 안녕하세요

밤에

  • 안녕하세요
  • 안녕히 주무세요
  • 안녕히 계세요


HTML 소스는 이렇게 되겠죠? 

<html><head><title>Greeting</title></head><body>

<h1>Greeting</h1>

<p>아침과 낮에</p>

<ul>

<li>좋은 아침입니다.</li>

<li>안녕하세요</li>

</ul>

<p>밤에</p>

<ul>

<li>안녕하세요</li>

<li>안녕히 주무세요</li>

<li>안녕히 계세요</li>

</ul>

</body></html>


메인 클래스가 다음과 같이 작성되었다고 가정하고, 이제 내용을 채워 봅시다.
여기서 Director 클래스는 result를 작성(construct)하는 일을 하며 Main클래스는 Director 클래스에게 할 일을 위임합니다.

Main.java
public class Main { public static void main(String[] args) { String option="plain"; //"plain" or "html" Director director = new Director(); 

if (option.equals("plain")) { String result = ""; // TODO: director를 이용하여 result 구성 System.out.println(result); } else if (option.equals("html")) { // TODO: director를 이용하여 result를 HTML파일로 저장 System.out.println("HTML 파일이 작성되었습니다."); } } }


Before) 패턴 미적용
먼저 그냥 평소에 하던 대로 코드를 작성해 볼까요?
다음과 같이 Main 클래스를 바꾸어 봅시다.

Main.java
public
class Main { public static void main(String[] args) { String option="plain"; //"plain" or "html"
Director director = new Director(); 
if (option.equals("plain")) { String result = director.constructText();
System.out.println(result); } else if (option.equals("html")) {
director.constructHTML(); System.out.println(filename + "이 작성되었습니다."); } } }

Director 클래스는 다음과 같이 생겨 먹었습니다.
(각 메쏘드에 채워질 Director 클래스의 전체 소스는 첨부파일을 참조하세요.)

Director.java

public class Director { public Director() { } public String constructText() { String result = ""; // TODO 텍스트 형태로 결과를 반환 return result; } public String constructHTML() { String filename = ""; // TODO HTML형태로 내용을 저장하고 파일명을 반환 return filename; } }

그런데 만약 Text나 HTML이 아니라 PDF형태로 또 결과를 저장해서 한다고 해 봅시다.
그러면 Director 클래스에 constructPDF() 이런 함수를 또 추가해야 겠죠? 이런 방식에는 다음과 같은 문제가 존재합니다.

1. 요구사항이 추가될 때 마다 Director클래스를 수정해야 합니다.
2. Director 클래스의 크기가 점점 커져서 관리가 어렵습니다. 
   즉, 이 클래스는 기능욕심(Feature Envy)을 가진 클래스가 됩니다. 클래스는 자기가 해야할 최소한의 일을 하는 것이 좋습니다.
3. 
또한 Director클래스의 크기가 커져 읽기에도 힙듭니다. 


After) Builder 패턴을 적용 

이제 이 지저분한 Director 클래스를 정리해서 깔끔하게 만들어 봅시다.
우리가 출력해야할 결과를 보면 타이틀, 소제목 및 각 항목으로 나눌 수 있습니다.
Director클래스는 문서의 각 부분을 조합하는 일만 하고, 실제 각 부분이 어떻게 구성되는 지는 각각의 클래스(TextBuilder, HTMLBuilder)를 만들어 위임시킵니다.

Main.java
public class Main { public static void main(String[] args) { String option="plain"; //"plain" or "html" if (option.equals("plain")) { Director director = new Director(new TextBuilder()); String result = (String) director.construct(); System.out.println(result); } else if (option.equals("html")) { Director director = new Director(new HTMLBuilder()); String filename = (String) director.construct(); System.out.println(filename + "이 작성되었습니다."); } }


Director.java
public class Director { private Builder builder; public Director(Builder builder) {
  // Builder의 하위 클래스의 인스턴스가 제공되기 때문에 builder 필드에 보관해 둔다.
this.builder = builder; } public Object construct() { builder.makeTitle("Greeting"); builder.makeString("아침과 낮에"); builder.makeItems(new String[] { "좋은 아침입니다.", "안녕하세요", }); builder.makeString("밤에"); builder.makeItems(new String[] { "안녕하세요", "안녕히 주무세요", "안녕히 계세요", }); return builder.getResult(); } }

Builder.java
public abstract class Builder { public abstract void makeTitle(String title); public abstract void makeString(String str); public abstract void makeItems(String[] items); public abstract Object getResult(); }

TextBuilder.java
public class TextBuilder extends Builder { private StringBuffer buffer = new StringBuffer(); // 이 필드에 문서를 구축한다. public void makeTitle(String title) { // 일반 텍스트에서의 타이틀 buffer.append("==============================\n"); // 장식선 buffer.append("" + title + "』\n"); // 『』가 붙은 타이틀 buffer.append("\n"); // 공란 } public void makeString(String str) { // 일반 텍스트에서의 문자열 buffer.append('' + str + "\n"); // ■이 붙은 문자열 buffer.append("\n"); // 공란 } public void makeItems(String[] items) { // 일반 텍스트에서의 항목 for (int i = 0; i < items.length; i++) { buffer.append("" + items[i] + "\n"); // ●이 붙은 항목 } buffer.append("\n"); // 공란 } public Object getResult() { // 완성된 문서 buffer.append("==============================\n"); // 장식선 return buffer.toString(); // StringBuffer를String을 변환 } }

HTMLBuilder.java
import java.io.*; public class HTMLBuilder extends Builder { private String filename; // 작성할 파일명 private PrintWriter writer; // 파일에 기술할 PrintWriter public void makeTitle(String title) { // HTML 파일에서의 타이틀 filename = title + ".html"; // 타이틀을 토대로 파일명을 결정 try { writer = new PrintWriter(new FileWriter(filename)); // PrintWriter만든다. } catch (IOException e) { e.printStackTrace(); } writer.println("<html><head><title>" + title + "</title></head><body>"); // 타이틀을 출력 writer.println("<h1>" + title + "</h1>"); } public void makeString(String str) { // HTML 파일에서의 문자열 writer.println("<p>" + str + "</p>"); // <p>태그에서 출력 } public void makeItems(String[] items) { // HTML 파일에서의 항목 writer.println("<ul>"); // <ul>과<li>에서 출력 for (int i = 0; i < items.length; i++) { writer.println("<li>" + items[i] + "</li>"); } writer.println("</ul>"); } public Object getResult() { // 완성된 문서 writer.println("</body></html>"); // 태그를 만든다. writer.close(); // 파일을 클로즈 return filename; // 파일명을 반환한다. } }

이해 되셨나요? 각 부분을 만드는 일을 하는 클래스(XXXBuilder)와 이를 조립해서 쓰는 클래스(Director)가 분리되었습니다. 
이제 PDF문서로 결과를 저장하는 일이 추가되더라도 Director클래스를 수정할 필요없이 Builder클래스를 상속받은 PDFBuilder클래스를 하나 만들고 Director클래스 객체를 생성할 때 생성자의 인자로 넘겨주기만 하면 되는 군요. ^^

각 클래스가 하는 일도 명확하고, 클래스 자체의 크기도 줄어 들어 코드를 읽기도 쉬워 졌습니다.


이번 강좌에서는 GoF의 분류에 따라 생성(Creational) 디자인 패턴 중 하나인 Abstract Factory 패턴을 소개합니다.


Factory Method 패턴에서는 상속을 이용하여 객체 생성을 파생클래스에게 위임하여 실제 생성되는 객체는 파생 클래스가 결정하도록 하였습니다. 

이와 유사한 디자인 패턴으로 Abstract Factory 패턴이 있습니다.
Abstract Factory 패턴은 Factory와 Product를 추상화 시켜 Abstract 클래스를 선언하고,
실제 구현은 그 하위 클래스에게 위임하도록 합니다.

예를 들어 다음과 같은 요구사항을 구현해 봅시다.
 

아이스크림을 만드는 공장(IcecreamFactory)이 있습니다.

이 공장에는 딱딱한 하드(Hard)를 만드는 라인과 부드러운(Soft) 아이스크림을 만드는 라인이 있습니다. (여름엔 하드가 시원하고 맛있네요 :) )

각 제품군에는 어떤 재료(Chocolate, Banana)를 사용하는 냐에 따라 실제 제품이 결정됩니다.


먼저 패턴을 적용하지 않고 위 요구사항을 만족하도록 각 제품을 생성하도록 클래스를 작성해 봅시다.

Main.java import icecream.*; public class Main { public static void main(String[] args) { createAllHardIcecreams(); createAllSoftIcecreams(); } private static void createAllHardIcecreams() { ChocolateHard ch = new ChocolateHard(); ch.make(); BananaHard bh = new BananaHard(); bh.make(); } private static void createAllSoftIcecreams() { ChocolateSoft cs = new ChocolateSoft(); cs.make(); BananaSoft bs = new BananaSoft(); bs.make(); } } ChocolateHard.java package icecream; public class ChocolateHard { public void make() { System.out.println("ChocolateHard를 만듭니다."); } } ChocolateSoft.java package icecream; public class ChocolateSoft { public void make() { System.out.println("ChocolateSoft를 만듭니다."); } } BananaHard.java package icecream; public class BananaHard { public void make() { System.out.println("BananaHard를 만듭니다."); } } BananaSoft.java package icecream; public class BananaSoft { public void make() { System.out.println("BananaSoft를 만듭니다."); } }



 

뭔가 중복된 구조가 보이시나요?
먼저 Hard와 Soft 아이스크림을 반복해서 만들고 있고,
Hard/Soft에 대해 원재료가 뭐냐에 따라 또 세부적으로 제품이 만들어 지는 군요.

이 코드를 다듬어서 다음과 같은 구조를 가지도록 리팩토링을 해 봅시다.
Factory Method와 같이 각 클래스를 직접 생성하지 않고 인터페이스만 Main클래스에 노출시키도록 합시다.


 

사실 hardfactory, softfactory 이렇게 물리적으로 패키지가 나누어져 있기 때문에
각 패키지 내의 클래스명에는 Hard/Soft 이런 단어를 안 붙이는 게 맞지만 이해를 돕기위해 중복해서 붙여놓았습니다.

먼저 IcreamFactory클래스는 Abstract 클래스로서, 세부 Factory 클래스에서 어떤 일을 수행하는 지를 선언합니다.
실제로 어떤 Factory객체가 생성될 지는 getFactory클래스의 인자에 따라 달라집니다.
이렇게 Client(즉, Main클래스)에서 인자로 클래스명을 넘겨 사용하고자 하는 Factory객체를 생성하는 방법도 많이 사용됩니다.
getFactory함수를 사용하지 않는다면 다음과 같이 HardFactory와 SoftFactory클래스에 각각 createFactory함수를 static으로 선언해야 겠지요.

public static getHardFactory() { return new HardFactory(); }
일단 어떤 제품군을 생성할 지에 대한 Factory 객체를 얻었다면,
해당 공장에서 생산하는 제품을 얻어야 합니다.
공장에서 생산하는 제품은 인터페이스로 정의되어 있습니다.

public abstract ChocolateIcecream createChocolateIcecream(); public abstract BananaIcecream createBananaIcecream();
실제 제품은 공장 제품군에 맞게 각각 구현이 됩니다.
즉, Hard를 만드는 공장에서는 Hard 제품을, Soft 아이스크림을 만드는 공장에서는 Soft한 제품을 만들지요.

IcecreamFactory.java package hardfactory; import icecreamfactory.*; public class HardFactory extends IcecreamFactory { @Override public BananaIcecream createBananaIcecream() { return new BananaHard(); } @Override public ChocolateIcecream createChocolateIcecream() { return new ChocolateHard(); } } SoftFactory.java package softfactory; import icecreamfactory.*; public class SoftFactory extends IcecreamFactory { @Override public BananaIcecream createBananaIcecream() { return new BananaSoft(); } @Override public ChocolateIcecream createChocolateIcecream() { return new ChocolateSoft(); } }
실제 생성되는 제품인 BananaHard, ChocolateHard, BananaSoft, ChocolateSoft 객체는
그 재료가 무엇이냐에 따라 만들어 지는 과정이 다르기 때문에,
상위 객체(BananaIcecream, ChocolateIcecream)에서 제공한 인터페이스를 적당하게 구현하고 있습니다.

BananaIcecream.java package icecreamfactory; public abstract class BananaIcecream { public BananaIcecream() { mashBanana(); mixBanana(); } public abstract void mashBanana(); public abstract void mixBanana(); }
그럼 BananaHard 클래스를 볼까요?

BananaHard.java package hardfactory; import icecreamfactory.BananaIcecream; public class BananaHard extends BananaIcecream { @Override public void mashBanana() { System.out.println("Hard: mashBanana"); } @Override public void mixBanana() { System.out.println("Hard: mixBanana"); } }

전체 소스는 첨부 파일을 참고하세요.
이렇게 Abstract Factory 패턴을 적용하고 나면 Main 클래스에서는 다음과 같이 바뀔 겁니다.

Main.java import icecreamfactory.*; public class Main { public static void main(String[] args) { createAllHardIcecreams(); createAllSoftIcecreams(); } private static void createAllHardIcecreams() { IcecreamFactory factory = IcecreamFactory.getFactory("hardfactory.HardFactory"); factory.createChocolateIcecream(); factory.createBananaIcecream(); } private static void createAllSoftIcecreams() { IcecreamFactory factory = IcecreamFactory.getFactory("softfactory.SoftFactory"); factory.createChocolateIcecream(); factory.createBananaIcecream(); } }
마지막으로 Abstract Meothod 패턴이 사용되는 상황을 다시 정리해 보겠습니다.

-. 생성/구성/표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때

-. 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 표현하고 싶을 때


Abstract Meothod 패턴은 제품군을 쉽게 다른 것으로 대체할 수 있는 장점이 있지만,
새로운 종류의 제품군을 추가하기 어렵습니다.

예를 들어 딸기 맛이 나는 아이스크림을 추가하려고 하면 기존의 클래스를 많이 손봐야 합니다.

-. IcecreamFactory에 createStrawberryIcecream 인터페이스 추가

-. HardFactory, SoftFactory에 인터페이스 구현부 추가

-. hardfactory, softfactory 패키지에 StrawberryHard, StrawberrySoft 클래스 추가


따라서 제품군이 어느 정도 정해져 있고 잘 바뀌지 않는 상황에서 적용해야 추후 제품이 추가될 경우 코드를 수정하는 일이 줄어들겠습니다. :)


GoF의 분류에 따라 생성(Creational) 디자인 패턴 중 하나인 Factory Method 패턴을 소개합니다.


다음과 같이 문제상황이 주어졌습니다. 

사용자 이름을 멤버 변수로 가진 CreditCard클래스가 있고, CreditCard 객체가 사용될 때 어떤 operation(use())을 하고자 합니다.


처음에는 이렇게 간단히 구현할 겁니다.

Main.java
public class Main { public static void main(String[] args) { CreditCard card = new CreditCard("홍길동"); card.use(); } }

CreditCard.java
public class CreditCard { public static final String USED_MSG = "의 CreditCard를 사용합니다."; private String owner; public CreditCard(String owner) { System.out.println(owner + "의 CreditCard를 만듭니다."); this.owner = owner; } public void use() { System.out.println(owner + USED_MSG); } }

하지만 이후 카드 회사에서는 (연회비는 비싸지만) 보너스 포인트를 추가로 쌓아주는 

BonusCard를 발급하기로 했습니다.


이럴 경우는 Main.java 파일을 다음과 같이 수정해야 합니다. 왜냐하면 Main클래스와 이 클래스에서 객체를 생성하는 클래스는 강한 연관을 가지고 있기 때문입니다.

public class Main { public static void main(String[] args) { //CreditCard card = new CreditCard("홍길동"); BonusCard card = new BonusCard("나잘난"); card.use(); } }

그러면 이를 좀 더 객체지향적으로 바꾸어서 Main클래스에서 내부 객체 생성이 어떻게 되는 지 알 필요가 없이 인터페이스만 보고 사용할 수 있도록 바꾸려면 어떻게 해야 될까요?

다음과 같이 객체 생성 인터페이스를 외부에 공개하고 실제 객체가 생성되는 부분은 안으로 감추어서 구현하면 될 것입니다.

이 예제에서의 Factory Method 패턴은 사실 Template Method 패턴을 두 번 적용한 것입니다.
위키피디아에서는 그냥 객체 생성방법에 초점을 맞추기 위해 Product에는 Template Method를 적용하지 않았네요.

어쨋든 Factory Method 패턴과 Template Method 패턴은 거의 동일합니다.
- 행위 패턴인 Template Method 패턴은 중복을 없애고 코드 재사용을 위해 객체를 사용하는 방법에 초점을 맞추고 있는 반면, 
- Factory Method 패턴은  생성하고자 하는 객체의 클래스(Product)와 이를 생성하는 클래스(Factory)의 인터페이스만 공개하여 재사용성을 높입니다.

이 패턴의 구현은 다음과 같이 됩니다.

Main.java
public
class Main { public static void main(String[] args) { Factory factory = new CardFactory(); Product card1 = factory.create("홍길동"); Product card2 = factory.create("나잘난"); card1.use(); card2.use(); } }

Factory.java
package framework; public abstract class Factory { public final Product create(String owner) { Product p = createProduct(owner); return p; } protected abstract Product createProduct(String owner); }

Product.java
package
framework; public abstract class Product { public abstract void use(); }
 

CardFactory.java
package card; import framework.Factory; import framework.Product; public class CardFactory extends Factory { protected Product createProduct(String owner) { //return new CreditCard(owner); return new BonusCard(owner); } }

CreditCard.java
package card; import framework.Product; public class CreditCard extends Product { public static final String USED_MSG = "의 CreditCard사용합니다."; private String owner; public CreditCard(String owner) { System.out.println(owner + "의 CreditCard를 만듭니다."); this.owner = owner; } public void use() { System.out.println(owner + USED_MSG); } }

Main 클래스는 생성하고자 하는 XXXProduct를 직접 new로 생성하지 않고, XXXFactory의
createProduct() 인터페이스를 이용하여 생성합니다.
여기서 패키지가 물리적으로 framework와 card로 나누어져 있다는 것에 주목하길 바랍니다.
카드 객체가 필요한 쪽에서는 card 패키지의 내부에 있는 클래스를 세세히 몰라도 됩니다. 즉, Main 클래스와 약한 연관을 가집니다. 
framework 패키지 내의 Factory클래스를 상속받은 XXXFactory클래스를 이용하여 Factory 클래스에 공개된 인터페이스만 이용하면 객체를 생성할 수 있습니다.
실제로 어떤 객체가 생성되는 지는 XXXFactory에서 결정하고 있습니다.

이렇게 패키지를 물리적으로 나누어 놓으면 추가로 다른 Factory와 Product를 추가할 때에도 용이합니다. 예를 들어 Product를 상속받은 Robot 객체가 필요하고, 이 객체를 만들어 사용(use())하고자 한다면,
- robot 패키지를 하나 만들고,
- Product를 상속받은 Robot 클래스
- Factory를 상속받은 RobotFactory 클래스를 만들어 구현하면 됩니다.

물리적으로 완전히 분리되어 있기 때문에 해당 소스를 형상관리 할 때도 편리합니다. 즉, 필요한 소스를 추가/삭제/변경하고 이력관리를 용이하게 할 수 있다는 말이지요. 

Factory Method를 사용하는 상황을 다시 정리해 보겠습니다.

의도
객체를 생성하기 위해 인터페이스를 정의하지만, 어떤 클래스의 객체를 생성할 지에 대해서는 하위 클래스에서 결정 ==> 클래스 상속을 이용함

활용 예
-. 생성할 객체 타입을 예측할 수 없을 때 ==> 부모 클래스 타입을 이용
-. 생성할 객체의 명세를 하위 클래스에서 정의하고자 하는 경우
-. 객체 생성의 책임을 하위 클래스에 위임시키고 어느 하위 클래스가 위임했는지에 대한 정보를 은닉하고자 하는 경우 


Creational Patterns

객체를 생성하는 더 나은 방법을 제시

 

Factory Method

객체를 생성하는 interface를 정의

instance를 만들 class의 결정은 subclass가 담당함

class를 instance 화 하는 시점을 subclass 에서 수행하도록 지연시킴

 

Abstract Factory

구체적인 class를 지정하지 않고

관련성 있는 객체의 집합을 생성하거나

서로 독립적인 객체들의 집합을 생성할 수 있는 interface를 제공

 
Builder 
복잡한 인스턴스를 조립.
각 부분에 대한 구현부를 나누어 이를 조립하여 큰 기능을 수행함.

Prototype
클래스 이름을 지정하지 않고 인스턴스를 복사하여 생성.
- 종류가 너무 많아 한 개의 클래스로 할 수 없는 경우
- 클래스로부터 인스턴스를 생성하기 어려운 경우
- 프레임워크와 생성할 인스턴스를 분리하고 싶은 경우

Singleton

class의 instance는 오직 하나가 되도록 보장하고

이 instance에 접근하는 방법을 제공

 

Structural Patterns

객체를 어떻게 같이 합칠 것 인지 제시

 

Facade

subsystem의 interface 집합에 대해 하나의 통합된 interface를 제공

subsystem 사용이 쉽도록 하기 위해 높은 수준의 interface를 사용

 

Proxy

다른 객체로의 접근을 통제하기 위해 해당 객체의 대리자를 제공

 

Adapter

class의 interface를 다른 class가 사용할 수 있는 class로 변환함

호환성이 없는 interface로 연동이 불가능한 class를 개조하여 연동이 가능하게 함

 

Composite

부분, 전체 계층을 표현하기 위해 복합 객체를 tree 구조로 제공

client가 단일 객체와 복합 객체 모두를 동일하게 다룰 수 있도록 함

 

Decorator

객체에 동적으로 책임을 추가할 수 있도록 함

기능의 유연한 확증을 상속이 아닌 방법으로 가능하도록 함

Bridge

기능 계층과 구현 계층을 분리.

Flyweight

객체 생성을 최소화 하여 메모리 낭비를 제거.


Behavioral Patterns

 

객체가 어떻게 상호작용하고 기능을 분산하는지 서술

 

Strategy

알고리즘군이 존재할 경우, 각각의 알고르짐을 별도의 클래스로 캡슐화

알고리즘 교환이 client영향을 주지 않도록함

 

Command

request를 객체로 캡슐화

서로 다른 request를 parameter로 구분

 

Iterator

내부 구조를 노출하지 않고 복합 객체의 원소를 순차적으로 접근하는 방법을 제공

 

Observer

객체 사이의 1:다 관계의 종속성을 정의

한 객체의 상태가 변경될 경우 종속된 다른 객체에 통보하는 방법을 제공

State

Visitor


Template Method

Chain of Responsibility

Mediator

Memento


Interpreter


2002년 JAVA를 공부하면서 OOP 개념을 처음 접하고 SCJP 자격증도 땄지만,

코딩 수준은 순차적 프로그래밍에서 크게 벗어나지 못했습니다.

그리고 부산에 잠시 학원을 다녔을 때 구모 선생님의 권유로 디자인 패턴을 알게 되고 독학을 했었지요.

매번 패턴을 공부할 때 마다 느끼는 것은 다음과 같았습니다.

- 배우기 어렵다.
- 머리로는 이해를 하겠는데 저런 문제 상황이 닥쳐도 과연 저런 패턴을 적용해서 설계를 할까?
- GoF가 정리한 패턴이 23개인데 이것들만 학습해도 돌아서면 까먹는다.
 
최근에 디자인 패턴 외부 교육을 받고 나서는 이전에 혼자 공부할 때 보단 이해도가 더 높아졌지만, 자꾸 쓰지 않다 보면 까먹게 되는게 당연지사라고 생각되어 이번 기회에 패턴을 제 나름대로 정리해 보고자 합니다.

심책임님의 코멘트를 반영하여 패턴을 그냥 나열식으로 정리하는 게 아니라
각 패턴을 적용하기 전 나쁜 코드와 패턴을 적용한 코드를 비교하여 장단점을 정리하려 합니다.

틈나는 데로 GoF의 "Design Patterns: Elements of Reusable Object-Oriented Software"도 읽고 그들이 이런 패턴을 만들게 된 배경에 대해 좀 더 자세히 알아봐야 겠네요.




해당 메일 폴더에서,

보기 > 보기 설정 > 기타 설정 > 행 글꼴


출처 : http://keep2smile.tistory.com/262


요즘에 크롬이 하두 로딩속도가 빠르다 해서 한번 테스트겸해서 쓰고 있습니다. 속도 정말 빠르군요;; 

헌데 너무 기본에 충실한 나머지 부가기능이 없더군요.. 특히 ActiveX가 판을 치는 우리나라에선 파폭의 IE Tab 같은 플러그인이 있어야 주 브라우저로 사용을 합니다. 그래서 IE가 대부분 사람들의 주 브라우저죠. 그러나 크롬도 IE Tab 같은 플러그인이 있습니다. 설치법은 너무 간단합니다.

STEP 1아래 파일을 다운 받아 크롬이 설치된 디렉토리/Plugins 에 넣습니다.
비스타의 경우 : C:\Users\자신의 계정\AppData\Local\Google\Chrome\User Data\Default\Plugin Data\ 에 복사해서 넣으시면 됩니다.
STEP 2위 경로에서 cmd창을 이용하여 레지스트리 등록을 해줍니다.
C:\Users\자신의 계정\AppData\Local\Google\Chrome\User Data\Default\Plugin Data\regsvr32.exe npmeadax.dll
예)

STEP 3크롬 브라우저에서 북마크를 추가합니다.
CTRL+B를 눌러서 북마크바를 보이게 한 후 북마크바에서 오른쪽 마우스를 클릭하여 페이지 추가를 선택합니다.


STEP 4제목과 링크를 입력합니다.
링크는
javascript:(function () { var lh=location.href; if( !lh || lh.match(/^(javascript|about):/i) ) return; document.write('<html><head><title> '+(document.title?document.title:lh).replace(/</g,'<').replace(/>/g,'>')+' - using Internet Explorer rendering<\/title><\/head><body style=\'margin:0px;padding:0px;\'>
<script type=\'text/javascript\'>var script = document.createElement(\'script\'); 
var embed = \'<embed type=\\\'application\/x-meadco-neptune-ax\\\' 
width=\\\'100\x25\\\' height=\\\'100\x25\\\' param-location=\\\'' + lh + '\\\'><\/embed>\'; script.setAttribute(\'src\',\'data:text/javascript,document.write(embed);\'); document.body.appendChild(script);<\/script><\/body><\/html>'); })();
를 넣으시면 됩니다.

STEP 5사용법
사용법도 간단합니다. ActiveX를 필요로 하는 사이트 (예, 각 은행 사이트..)에 들어가셔서 위에서 추가한 북마크를 누르시기만 하면 됩니다. 


구글 칼렌다에 음력 입력 기능이 없어 이것 저것 어플도 깔아보고 했는데, 

무료 앱은 버그가 많아 짱나서 결국 이 방법으로 하기로 결정했습니다.
내 일정을 ical형식으로 만들어서 다시 구글 칼렌다에서 이를 가져오면 됩니다.

출처: http://bangley.egloos.com/tb/5363847

ex) 매년 음력2010년10월1일을 '제사' 라고 지정하고 싶다면?
http://korean-lunar-ical.appspot.com/20101001.ics?summary=제사

 
이렇게 하면 해당 날짜의 summary로 된 일정을 50년치 만들어 줍니다.
알림 기능이 미리 설정되지 않고, 한 번에 편집/삭제가 되지 않아 불편하긴 하지만 
구글 칼렌다 자체에서 음력 지원이 될 때 까진 이렇게 라도 쓰는 수 밖에요.. 


MS는 기능도 별로 나아진 것도 모르겠는데 왜 UI를 바꿔서 사람 헷갈리게 하나 모르겠습니다.
걍 단축키를 더 많이 외워서 쓰기로 했습니다.


출처: http://moowoo.x-y.net/bbs/bbs/board.php?bo_table=com_story&wr_id=460&page=3


범주명령단축키오피스공통

파일열기Ctrl + OO
열려진 창들 간의 전환Alt + TabO
문서 닫기Ctrl + WO
프로그램 창 닫기Alt + F4O
저장Ctrl + SO
다른 이름으로 저장F12O
새 문서 생성Ctrl + NO
인쇄Ctrl + PO
인쇄 미리 보기Ctrl + F2O
미세하게 이동Ctrl + 상하좌우 방향키 (↑↓← →) O
미세하게 이동 / 그리기Alt + 마우스 드래그O

편집잘라내기Ctrl + XO
복사Ctrl + CO
붙여넣기Ctrl + VO
전체 선택Ctrl + AO
찾기Ctrl + FO
바꾸기Ctrl + HO
취소(undo)Ctrl + ZO
재실행(redo)Ctrl + Y 또는 F4O
마지막 찾기 작업 반복Shift + F4O
선택한 개체 복제Ctrl + DO
선택한 슬라이드 복제Ctrl + Dppt
슬라이드에서 모든 개체 선택Ctrl + Appt
여러 슬라이드 보기에서 모든 슬라이드 선택Ctrl + Appt
개요 탭에서 모든 개요 텍스트 선택Ctrl + Appt
서식복사Ctrl + Shift + C 한 후 Ctrl + Shift + V
cf. Ctrl + C / Ctrl + V
O
그룹Ctrl + Shift + GO
그룹해제Ctrl + Shift + HO

텍스트 편집
Tip
Shift: 블록선택
Ctrl: 커서 이동
단락은 나누지 않고 줄만 나누기Shift + Enter 
텍스트 속한 단락 전체 선택Ctrl + 텍스트 클릭 
텍스트의 특정 부분 선택, 최초 클릭한 부분에서 두번째 클릭한 곳까지 선택첫 선택부분 클릭 후 Shift + 마지막 선택 부분 클릭 
오른쪽으로 한 글자 블록 선택Shift + →O
왼쪽으로 한 글자 블록 선택Shift + ←O
단어 끝까지 블록 선택Ctrl + Shift + →O
단어의 시작까지 블록 선택Ctrl + Shift + ←O
한 줄 위로 블록 선택Shift + ↑O
한 줄 아래로 블록 선택Shift + ↓O
텍스트 상자 끝 글자까지 블록 선택Ctrl + Shift + EndO
텍스트 상자 첫 글자까지 블록 선택Ctrl + Shift + HomeO
텍스트 상자에 있는 모든 테스트 블록 선택Ctrl + AO
왼쪽으로 한 글자 커서 이동O
오른쪽으로 한 글자 커서 이동O
위쪽으로 한 줄 커서 이동O
아래쪽으로 한 줄 커서 이동O
왼쪽으로 한 단어 커서 이동Ctrl + ← O
오른쪽으로 한 단어 커서 이동Ctrl + →O
줄의 맨 끝으로 커서 이동EndO
줄의 맨 처음으로 커서 이동HomeO
한 단락 위로 커서 이동Ctrl + ↑O
한 단락 아래로 커서 이동Ctrl + ↓O
문서 끝으로 (텍스트 상자에 커서가 있는 경우 텍스트 상자 끝으로)Ctrl + EndO
문서 시작으로 (텍스트 상자에 커서가 있는 경우 텍스트 상자 시작으로)Ctrl + HomeO
왼쪽 한 글자 삭제BackSpaceO
왼쪽 한 단어 삭제Ctrl + BackSpaceO
오른쪽 한 글자 삭제DleteO
오른쪽으로 한 단어 삭제Ctrl + DeleteO

개요개요 탭에서 단락 수준 올리기Alt + Shift + ← 키 또는 Shift + Tabppt
개요 탭에서 단락 수준 내리기Alt + Shift + → 키 또는 Tabppt
개요 탭에서 단락 위로 이동하기Alt + Shift + ↑ppt
개요 탭에서 단락 아래로 이동하기Alt + Shift + ↓ppt
개요 탭에서 첫째 수준만 표시Alt + Shift + 1ppt
개요 탭에서 선택된 슬라이드의 하위 수준 모두표시하기Alt + Shift + +ppt
개요 탭에서 선택된 슬라이드의 첫째 수준만 표시하기Alt + Shift + -ppt
개요 탭에서 모든 텍스트 표시하기Alt + Shift + A 또는 Alt + shift + 9ppt

보기눈금 표시/숨기기Shift + F9ppt
안내선 표시/숨기기Alt + F9ppt
눈금 및 안내서 대화상자 표시Ctrl + Gppt
개요/슬라이드 탭 전환Ctrl + Shift + Tabppt
작업창 표시Ctrl + F1O
슬라이드 디자인 작업창 표시Alt + Sppt
슬라이드창 최소화Ctrl + F9엑셀 가능
슬라이드 화면/슬라이드탭 /선택개체 확대, 축소 보기
: 표준도구 모음의 확대/축소 이용을 좀더 간편하게
Ctrl + 마우스휠 상하 조정O

서식굵게Ctrl + BO
기울임꼴Ctrl + IO
밑줄Ctrl + UO
아래첨자Ctrl + =O
위첨자Ctrl + Shift + =O
대소문자 전환Shift + F3O
텍스트 상자 테두리 선택Escppt
왼쪽 맞춤Ctrl + LO
가운데 맞춤Ctrl + EO
오른쪽 맞춤Ctrl + RO
글꼴 크기 크게Ctrl + Shift + > 또는 Ctrl + ]O
글꼴 크기 작게Ctrl + Shift + < 또는 Ctrl + [O
폰트 크기 입력Ctrl + Shift + P 한 후 폰트 수치 입력, 엔터O
폰트 형태 선택Ctrl + Shift + F 한 후에 상하 방향키(↑↓)로 선택, 엔터O
글꼴 대화 상자 표시Ctrl + Tppt
개체 서식 대화 상자 표시개체 더블 클릭O

삽입새 슬라이드Ctrl + Mppt
하이퍼링크 삽입Ctrl + KO

다음 셀로 이동TabO
이전 셀로 이동Shift + TabO
다음 행으로 이동아래쪽 방향 키 ↓O
이전 행으로 이동위쪽 방향 키 ↑O
셀에 탭 삽입Ctrl + TabO
표 아래에 새 행 추가마지막 행 끝에서 TabO

도구맞춤법 검사F7O

슬라이드 쇼슬라이드 쇼 실행F5ppt
현재 슬라이드부터 슬라이드 쇼Shift + F5ppt
펜 기능 실행Ctrl + Pppt
다음 슬라이드로 이동N, Enter, Page Down, → (right arrow), ↓(down arrow), 또는 Spacebar 또는 마우스 클릭하기ppt
이전 슬라이드로 이동P, Page Up, ← (left arrow), ↑(up arrow), 또는 Back Spaceppt
펜 기능으로 작성한 내용 지우기E    cf. Ctrl + Eppt
펜 기능 중지Escppt
펜에서 화살표 포인터로 전환Ctrl + Appt
펜 또는 화살표 포인터 감추기Ctrl + Hppt
감춘 화살표 다시 나타내기Ctrl + Appt
하이퍼링크가 있는 곳으로 전환하기(실행)Tab (Enter)ppt
화면을 검정으로 설정B 또는 키보드자판 . (마침표)ppt
화면을 흰색으로 설정W 또는 키보드자판 , (쉼표)ppt
슬라이드 쇼 일시 중지 / 재실행S 또는 + (plus sign)ppt
원하는 슬라이드로 이동슬라이드 번호 입력 후, Enterppt
슬라이드 쇼에서 첫 번째 슬라이드로 이동Home 또는 좌우 마우스 동시에 2초간 누르기 ppt
마지막 슬라이드로 이동Endppt
슬라이드 쇼 도움말 표시F1ppt

도움말도움말 표시F1O


출처: http://webkebi.zany.kr:9003/board/bView.asp?bCode=11&aCode=1707&cBlock=0&cPageNo=1&sType=0&sString=


c:\Program Files\Vim\_vimrc 파일을 열어
아래 설정 구문 중, 원하는 것만 집어넣으면 된다.


syntax on              # 언어에 따른 자동 문법, 구문의 색을 다르게 표시
filetype on            # 파일 종류를 자동으로 인식
colorscheme torte      # 컬러스킴을 변경
set ru                 # 화면 우측 하단에 현재 커서의 위치(줄,칸)를 표시 (ruler)
set sc                 # 완성중인 명령을 표시
set vb                 # 키를 잘못눌렀을 때 삑 소리를 내는 대신 번쩍임 (visualbell)
set hls                # 검색된 스트링의 하이라이트 (hlsearch)
set ci                 # C 형태의 들여쓰기 (cindent)
set ai                 # 자동 들여쓰기 (autoindent)
set si                 # 좀더 똑똑한 들여쓰기 (smartindent)
set sw=4               # 자동들여쓰기를 4칸으로 설정 (shift width)
set ts=4               # TAB 간격을 4칸으로 설정 (tab stop)
set scs                # 똑똑한 대소문자 구별 기능 사용
set hi=50              # 명령어 기록을 남길 개수 지정 (history)
set cul                # 커서가 있는 라인 하이라이트

# GUI Mode 일 때만 수행...
if has("gui_running")
    set lines=56
    set columns=150
    set gfn=GulimChe
endif

set gfn=GulimChe 구문 대신 아래 구문을 써도 된다.
set guifont=돋움체:h9:cHANGEUL


==================
==== 추가 내용 ====
==================

출처 : http://namomo.egloos.com/1660114

""""""""""""""""""""""""""""""""""""""""""""""""
" GUI 설정
""""""""""""""""""""""""""""""""""""""""""""""""
" 폰트 설정. 폰트와 폰트 크기를 지정한다.
if has( "gui_running" )

set gfn=Consolas:h14

" set gfn=ProFontWindows:h12

" set gfn=gulimche:h12
" set gfn=sans-serif12
" set gfn=Lucida_Console:h12
" set gfn=돋음체12
" set gfn=Terminal12

" 초기 VI 시작시 크기 설정 w * h
au GUIEnter * winsize 84 30

" 초기 VI 시작 위치 설정
"au GUIEnter * winpos 550 0
endif



""""""""""""""""""""""""""""""""""""""""""""""""

" VI 기본 설정

""""""""""""""""""""""""""""""""""""""""""""""""

" 백업 파일을 만들지 않습니다.

set nobackup


" 자동 들여쓰기를 설정합니다.

set ai


" 경고 소리를 화면 깜빡임으로 대체

set visualbell


" 들여쓰기 폭을 정합니다.

set shiftwidth=4


" 탭의 폭을 정합니다.

set tabstop=4


" 탭을 스페이스로 대체합니다.

"set et


" C의 구문에 맞게 들여쓰기 합니다

set cindent


" 라인수를 표시해 줍니다

set nu


" 각 파일에 해당하는 문법(Syntax)를 적용시켜줍니다.

" C나 Java등 사용시 색상등..

syntax on



" 파일 편집시 undo 할수 있는 최대 횟수 설정

set history=100


" 함수 닫기표시

set sm


" 타이핑시 마우스 커서 감추기

"set mousehide


" 최소한 2줄 이하로는 자동 스크롤

set scrolloff=2


" 정의되어진 색상을 선택해서 보여줍니다

colors desert



" ESC키를 누르면 한글 모드가 해제

" 입력모드에서 이전 언어 설정 모드 유지

inoremap <ESC> <ESC>:set imdisable<CR>

nnoremap i :set noimd<CR>i

nnoremap I :set noimd<CR>I

nnoremap a :set noimd<CR>a

nnoremap A :set noimd<CR>A

nnoremap o :set noimd<CR>o

nnoremap O :set noimd<CR>O