개요

 한 그룹을 이루는 여러 회원들이 서로 업무를 파악하며 관여하거나 어떤 일을 처리할 때 각자 의견을 주장한다면 해당 그룹은 목적을 이뤄나가기에 많은 비용이 소모될 것이다. 이런 상황을 해결하기 위해 조정자, 중개인을 두고 의사결정과 상황판단을 중개인을 통해서만 처리함으로 그룹의 효율을 높일 수 있다. 

 위와 같은 경우를 프로그래밍에 적용한 패턴이 바로 Mediator(중개인, 조정자) 패턴이다.

 

 

역할

Mediator 역할

 Colleague 역할과 통신을 해서 조정하기 위한 인터페이스를 정의한다. 예제에서는 Mediator 인터페이스.

 

ConcreteMediator 역할

 Mediator 역할이 정의한 인터페이스를 구현해서 실제 조정자 역할을 한다. 예제에서는 LoginFrame 클래스.

 

Colleague 역할

 Mediator 역할과 통신을 실행할 인터페이스를 정의한다. 예제에서는 Colleague 인터페이스.

 

ConcreteColleague 역할

 Colleague 역할이 정의한 인터페이스를 구현한다. 그룹의 회원과 같은 역할. 예제에서는 ColleagueButton, ColleagueTextField, ColleagueCheckbox 클래스.

 

이득 및 주의사항

  객체지향 프로그래밍에선 모듈화를 통해 업무를 분산시켜 처리하는 경우를 쉽게 볼 수 있다. 그런데 Mediator 패턴은 문제처리를 한 모듈로 집중시키는 패턴처럼 보인다. 하지만 자세히 보면 기능은 각 모듈로 분산시키고, 이를 가지고 업무를 처리하는 로직만 집중시킨다는 것을 알 수 있다. 이렇게 부분적으로 분산시켜야 할 기능과 집중시켜야 하는 작업을 효과적으로 분리 하면 로직이 수정되거나 시스템의 문제가 발생했을 때 더 효과적으로 대처할 수 있는 이점이 있다.

 

  ConcreteColleague 역할은 화면(애플리케이션) 구성에서 자유로운 부분이기 때문에 재사용이 가능해서 새로운 화면이나 구성을 짤 때 사용할 수 있지만, ConcreteMediator는 화면(구성)과 종속적이기 때문에 애플리케이션에 종속적이다. 때문에 새로운 화면(구성)을 작성하기 위해서 ConcreteMediator의 재사용은 어렵고 새로운 ConcreteMediator를 구성하는게 오히려 비용이 저렴할 수 있다.

 

 

예제 코드

//Mediator 역할
package mediator;

public interface Mediator {
	public abstract void createColleagues();
	public abstract void colleagueChanged();
}


//ConcreteMediator 역할
package mediator;

import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LoginFrame extends Frame implements ActionListener, Mediator{
	private ColleagueCheckbox checkGuest;
	private ColleagueCheckbox checkLogin;
	private ColleagueTextField textUser;
	private ColleagueTextField textPass;
	private ColleagueButton buttonOK;
	private ColleagueButton buttonCancel;
	
	public LoginFrame(String title) {
		super(title);
		
		setBackground(Color.lightGray);
		setLayout(new GridLayout());
		createColleagues();
		add(checkGuest);
		add(checkLogin);
		add(new Label("Username : "));
		add(textUser);
		add(new Label("Password : "));
		add(textPass);
		add(textPass);
		add(buttonOK);
		add(buttonCancel);
		
		colleagueChanged();
		pack();
		show();
	}
	
	public void createColleagues() {
		CheckboxGroup g = new CheckboxGroup();
		checkGuest = new ColleagueCheckbox("Guest", g, true);
		checkLogin = new ColleagueCheckbox("Login", g, false);
		textUser = new ColleagueTextField("", 10);
		textPass = new ColleagueTextField("", 10);
		textPass.setEchoChar('*');
		buttonOK = new ColleagueButton("OK");
		buttonCancel = new ColleagueButton("Cancel");
		
		checkGuest.setMediator(this);
		checkLogin.setMediator(this);
		textUser.setMediator(this);
		textPass.setMediator(this);
		buttonOK.setMediator(this);
		buttonCancel.setMediator(this);
		
		checkGuest.addItemListener(checkGuest);
		checkLogin.addItemListener(checkLogin);
		textUser.addTextListener(textUser);
		textPass.addTextListener(textPass);
		buttonOK.addActionListener(this);
		buttonCancel.addActionListener(this);
		
	}
	
	//Colleague에서 통지로 Colleague의 유/무효를 판단한다.
	public void colleagueChanged() {
		if(checkGuest.getState()) {
			textUser.setColleagueEnabled(false);
			textPass.setColleagueEnabled(false);
			buttonOK.setColleagueEnabled(true);
		} else {
			textUser.setColleagueEnabled(true);
			userpassChanged();
		}
	}
	
	//textUser나 textPass의 변경이 발생시 각 Colleague의 유/무효를 판단한다.
	private void userpassChanged() {
		if(textUser.getText().length() >0) { 
			textPass.setColleagueEnabled(true);
			if(textPass.getText().length() > 0) {
				buttonOK.setColleagueEnabled(true);
			} else {
				buttonOK.setColleagueEnabled(false);
			}
		} else {
			textPass.setColleagueEnabled(false);
			buttonOK.setColleagueEnabled(false);
		}
	}
	
	public void actionPerformed(ActionEvent e) {
			System.out.println(e.toString());
			System.exit(0);
	}
}


//Colleague 역할
package mediator;

public interface Colleague {
	public abstract void setMediator(Mediator mediator);
	public abstract void setColleagueEnabled(boolean enabled);
}


//ConcreteColleague 역할1
package mediator;

import java.awt.Color;
import java.awt.TextField;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;

public class ColleagueTextField extends TextField implements TextListener, Colleague{
	private Mediator mediator;
	
	public ColleagueTextField(String text, int columns) {
		super(text, columns);
	}
	
	public void setMediator(Mediator mediator) {
		this.mediator = mediator;
	}
	
	public void setColleagueEnabled(boolean enabled) {
		setEnabled(enabled);
		setBackground(enabled ? Color.white : Color.lightGray);
	}
	
	public void textValueChanged(TextEvent e) {
		mediator.colleagueChanged();
	}
}


//ConcreteColleague 역할2
package mediator;

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague{
	private Mediator mediator;
	
	public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) {
		super(caption, group, state);
	}
	
	public void setMediator(Mediator mediator) {
		this.mediator = mediator;
	}
	
	public void setColleagueEnabled(boolean enabled) {
		setEnabled(enabled);
	}
	
	public void itemStateChanged(ItemEvent e) {
		mediator.colleagueChanged();
	}
}


//ConcreteColleague 역할3
package mediator;

import java.awt.Button;

public class ColleagueButton extends Button implements Colleague{
	private Mediator mediator;
	public ColleagueButton(String caption) {
		super(caption);
	}
	
	public void setMediator(Mediator mediator) {
		this.mediator = mediator;
	}
	
	public void setColleagueEnabled(boolean enabled) {
		setEnabled(enabled);
	}
}


//Tester
package mediator;

public class MediatorTester {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new LoginFrame("Mediator Sample");
	}

}

'디자인 패턴' 카테고리의 다른 글

18. Memento 패턴  (0) 2020.12.30
17. Observer 패턴  (0) 2020.12.29
15. Facade 패턴  (0) 2020.12.26
14. Chain of Responsibility 패턴  (0) 2020.12.22
13. Visitor 패턴  (0) 2020.12.17

개요

프로그램이 커지면서 많은 클래스가 작성되면 그 관계도 서로 복잡하게 된다. 때문에 어떤 클래스를 사용할 경우 클래스 간 관계를 정확히 이해하고 사용할 필요가 있는데, 그 처리를 위해 어떠한 창구(=Facede)를 두는 것이 Facade 패턴이다.

 창구를 통해 시스템 내부의 복잡한 관계를 정리해서 외부에서는 단순한 인터페이스로 이용이 가능하도록 해준다.

 

 예제에서 Facede 역할을 하는 PageMaker 클래스는 외부에서는 단순히 makeWelcomePage() 메서드만을 인터페이스로 제공한다. 실제로 내부에서 시스템을 구성하는 요소들(Database클래스, HtmlWriter클래스)을 어떤 관계와 순서로 호출하는지는 외부에서 알 수가 없고, 알 필요도 없는 것이다.

역할

Facade 역할

 시스템이 제공하는 많은 기능(역할)들에 대해 단순한 인터페이스를 제공하는 창구 역할을 한다. 예제에서는 PageMaker 클래스.

 

시스템을 구성하는 역할들

  시스템을 구성하는 역할들은 각기 다른 기능이나 작업을 하며 Facade 역할에게 호출된다. 각각의 역할들은 Facade 역할을 특별히 고려하진 않는다. 예제에서는 Database클래스, HtmlWriter 클래스.

 

Client 역할

  Facade 역할을 이용하는 역할로 단순한 인터페이스로 기능을 사용할 수 있다. 예제에서는 Main 클래스.

 

이득 및 주의사항

 Facade의 핵심은 복잡한 구조를 단순하게 보여주는 것이다. 특히 외부에서 사용하는 인터페이스(API)를 단순하게 하는 것이다. 외부에서 단순한 API만으로 접근한다는 말은 약한 결합을 지향한다는 의미고 결과적으로 Facade로 구성된 시스템이나 모듈집합의 독립성이나 재사용성을 높여준다.

 

 Facade로 하위 패키지들을 묶고, 다시 그 Facade들을 묶는 상위 Facade 패턴을 적용하는 재귀적 구조를 사용하면 큰 시스템에서 다수의 모듈들을 보다 편리하게 관리할 수 있다.

 

 클래스들을 설계할 때 필드나 메소드의 접근지정자를 주의한다. 필요최소로 public을 지정해야 클래스의 수정이 쉬워진다.

 

예제 코드

//Facade 역할
package pagemaker;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PageMaker {
	private PageMaker() {
		
	}
	
	public static void makeWelcomePage(String mailaddr, String filename) {
		try {
			Properties mailprop = Database.getProperties("maildata");
			String username = mailprop.getProperty(mailaddr);
			HtmlWriter writer = new HtmlWriter(new FileWriter(filename));
			writer.title("Welcome to " + username + "'s page!");
			writer.paragraph(username + "의 페이지에 오신 걸 환영합니다.");
			writer.paragraph("메일을 기다리고 있습니다.");
			writer.mailto(mailaddr, username);
			writer.close();
			
			System.out.println(filename + " is created for " + mailaddr + " (" + username + ")");
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
}


//시스템을 구성하는 모듈 역할1
package pagemaker;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class Database {
	private Database() {
		
	}
	public static Properties getProperties(String dbname) {
		
		String filename = dbname+".txt";
		Properties prop = new Properties();
		try {
			prop.load(new FileInputStream(filename));
		} catch(IOException e) {
			System.out.println("Warning : " + filename + " is not found.");
		}
		
		return prop;
	}
}


//시스템을 구성하는 모듈 역할2
package pagemaker;

import java.io.IOException;
import java.io.Writer;

public class HtmlWriter {
	private Writer writer;
	public HtmlWriter(Writer writer) {
		this.writer = writer;
	}
	
	public void title(String title) throws IOException{
		writer.write("<html>");
		writer.write("<head>");
		writer.write("<title>"+title+"</title>");
		writer.write("</head>");
		writer.write("<body>\n");
		writer.write("<h1>"+title+"</h1>\n");
	}
	
	public void paragraph(String msg) throws IOException {
		writer.write("<p>"+msg+"</p>");
	}
	
	public void link(String href, String caption) throws IOException{
		writer.write("<a href=\"" + href + "\">" + caption + "</a>");
	}
	
	public void mailto(String mailaddr, String username) throws IOException{
		link("mailto : " + mailaddr, username);
	}
	
	public void close() throws IOException{
		writer.write("</body>");
		writer.write("</html>\n");
		writer.close();
	}
}


//Client 역할
import pagemaker.PageMaker;

public class Main {
	public static void main(String[] args) {
		PageMaker.makeWelcomePage("programo@programo.com", "welcome.html");
	}
}


'디자인 패턴' 카테고리의 다른 글

17. Observer 패턴  (0) 2020.12.29
16. Mediator 패턴  (0) 2020.12.28
14. Chain of Responsibility 패턴  (0) 2020.12.22
13. Visitor 패턴  (0) 2020.12.17
12. Decorator 패턴  (0) 2020.12.16

스프링이 Bean 객체를 자동으로 주입해주기 위해선 먼저 스프링이 특정 클래스를 Bean객체가 되는 대상으로 인식해야 한다. 이때 사용되는 것이 바로 ComponentScan의 역할.

 

 먼저 xml 명세를 기반으로 보면 servlet-context.xml 파일에

<context:component-scan base-package="com.programo90.app">

위와 같이 컴포넌트를 스캔할 기준점이 되는 package를 지정하고 있다. 해당 패키지(위 예에선 "com.programo90.app" ) 이하에서 @Component나 @Controller, @Service, @Repository 등의 어노테이션으로 컴포넌트 대상이다 표시가 된 모든 클래스들을 찾고 해당 클래스로 Bean객체를 생성해서 팩토리에서 관리하게 된다.

 

 

 java 파일로 스프링을 설정 할때는 

 @Configuration, @ComponentScan 두 가지 어노테이션을 선언하면 해당 Class가 스프링 설정파일이고 ComponentScan 설정을 담당한다고 인식하게 된다. 

@Configuration
@ComponentScan(basePackages="com.programo90.app")
public class ApplicationConfig {
}

 @ComponentScan(basePackage = "com.programo90.app") 과 같이 파라미터로 특정 패키지를 지정할 수 있고,

 @ComponentScan(basePackageClasses = Application.class) 와 같이 특정 클래스 이하부터로 범위를 지정할 수도 있다.

 

'스프링' 카테고리의 다른 글

Spring Encoding 설정  (0) 2021.01.07
IoC(Inversion of Control) 제어의역전 과 IoC Container  (0) 2020.11.27
DI(Dependency Injection) 의존성 주입  (0) 2020.11.26

 예전에 작성된 코드를 수정할 일이 있어서 작업을 하다가 마주한 에러.

 

 이클립스에 STS를 플러그인 하고 MAVEN을 사용하였는데, 분명 java 8로 환경설정을 하고 작업을 하였고 서버도 돌아가는데 프로젝트 위에 x 표시가 사라지지 않았다.

 그래도 이렇게 툴에서 표시라도 해주는 에러는 고마운 경우이기 때문에 Problems 탭을 보니 제목과 같은 에러 메시지가 출력되고 있었다. 에러 메시지만 보아도 어딘가에서 java 1.6이하로 환경설정이 되어 있는 것인데, 스프링으로 작업을 해보면 알지만 환경설정을 만져줘야하는 부분이 많고 복잡해서 구글링을 하면서 확인해야 할 부분을 정리했다.

 

1. 프로젝트명 - 우클릭 - Build Path - Configure Build Path

 JRE System Library가 java 1.7이상인지 확인하고 아닐 경우 Edit을 눌러 JRE 1.7이상으로 변경해준다.

 

2. 프로젝트명 - 우클릭 - Properties - Java Compiler

 Compiler compliannce level 이 1.7 이상인지 확인하고 변경한다.

3. 프로젝트명 - 우클릭 - Properties - Project Facets

 Java 버전이 1.7이상인지 확인하고 변경한다.

 

4. pom.xml 에서 maven-comfiler-plugin 을 검색해서 찾는다.

 <source>, <target> 태그가 1.7이상인지 확인하고 변경한다.

 

 네 가지를 확인/변경하고 프로젝트 우클릭 - MAVEN - Project update를 해준다.

 

 나는 이걸로 에러가 해결되었는데, 만약 이래도 안될 경우 workspace에서 java버전을 설정하는 부분을 전부 확인해봐야 할것같다.

 

 

 

개요

 이름 그대로 책임 떠넘기기 패턴은 어떤 문제가 주어졌을때 그 문제를 해결할 수 있는 다음사람에게 책임을 계속 떠넘겨가는 패턴이다. 구조는 문제를 해결할 수 있는 객체를 일정한 순서대로 연결해서 체인을 이뤄놓고, 체인의 첫 부분부터 문제를 던져서 각 객체가 자신이 처리할 수 없을 땐 다음 객체에게 문제를 전달하도록 구현한다. 결과적으로 어떤 요구가 왔을 때 그에 적절하게 대응할 수 있는 객체가 선택되도록 할 수 있는것이 Chain of Responsibility 패턴이다.

 

역할

Handler 역할

 문제를 해결하는 객체들의 인터페이스를 결정하는 역할. 예제에서는 Support 클래스이다.

 

ConcreteHandler 역할

 Handler가 정의해놓은 인터페이스에 따라 실제 문제를 해결하도록 기능을 구현하는 역할. 예제에서는 NoSupport, LimitSupport, OddSupport, SpecialSupport 클래스이다.

 

Client 역할

 ConcreteHandler 객체들로 이뤄진 체인 가장 앞에 객체에게 문제를 전달하는 역할. 예제에서는 Main클래스이다.

 

이득 및 주의사항

 문제를 제시하는 Client와 문제를 해결하는 ConcreteHandler를 유연하게 연결해주고 사슬에 따라 우선순위가 결정되기 때문에 ConreteHandler의 정보를 중앙집중적으로 저장하고 주어지는 문제에 따라 적절한 ConcreteHandler를 찾기위해 검색할 필요가 없다. 문제를 던지는 Client도 ConcreteHandler들의 정보를 알고 있을 필요도 없기에 ConcreteHandler의 독립성이 유지된다.

 

 Chain을 요구에 따라 동적으로 재편할 수도 있는데, 각 ConcreteHandler들은 독립성이 유지되고 Chain의 순서만 변경함으로써 쉽게 요구에 따른 대응관계 수정이 가능하다.

 

 구조를 보면 미루어 알 수 있듯이 문제 처리가 지연될 수 있다. Chain이 지나치게 길면 문제에 따라 적절한 처리자에게 도달하기까지 처리시간이 지연되어 속도가 저하될 수 있다.

 

예제

//Handler 역할

public abstract class Support {
	private String name;
	private Support next;
	
	public Support(String name) {
		this.name = name;
	}
	
	public Support setNext(Support next) {
		this.next = next;
		return next;
	}
	
	public final void support(Trouble trouble) {
		if(resolve(trouble)) {
			
		} else if(next != null) {
			next.support(trouble);
		} else {
			fail(trouble);
		}
	}
	
	protected abstract boolean resolve(Trouble trouble);
	
	protected void done(Trouble trouble) {
		System.out.println(trouble + " is resolve by " + this +".");
	}
	
	protected void fail(Trouble trouble) {
		System.out.println(trouble + " cannot be resolved.");
	}
}


//ConcreteHnadler역할
public class NoSupport extends Support{
	public NoSupport(String name) {
		super(name);
	}
	
	protected boolean resolve(Trouble trouble) {
		return false;
	}
}


public class LimitSupport extends Support{
	private int limit;
	
	public LimitSupport(String name, int limit) {
		super(name);
		this.limit = limit;
	}
	
	protected boolean resolve(Trouble trouble) {
		if(trouble.getNumber() < limit) {
			return true;
		} else {
			return false;
		}
	}
}

public class OddSupport extends Support{
	public OddSupport(String name) {
		super(name);
	}
	
	protected boolean resolve(Trouble trouble) {
		if(trouble.getNumber()%2 == 1) {
			return true;
		} else {
			return false;
		}
	}
}


public class SpecialSupport extends Support{
	private int number;
	public SpecialSupport(String name, int number) {
		super(name);
		this.number = number;
	}
	
	protected boolean resolve(Trouble trouble) {
		if(trouble.getNumber() == number) {
			return true;
		} else {
			return false;
		}
	}
}


//Client 역할

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Support alice = new NoSupport("Alice");
		Support bob = new LimitSupport("Bob", 100);
		Support charlie = new SpecialSupport("Charlie", 429);
		Support diana = new LimitSupport("Diana", 200);
		Support elmo = new OddSupport("Elmo");
		Support fred = new LimitSupport("Fred", 300);
		
		alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
		
		for(int i=0; i<500; i++) {
			alice.support(new Trouble(i));
		}
	}

}


//주어지는 문제 역할

public class Trouble {
	private int number;
	public Trouble(int number) {
		this.number = number;
	}
	
	public int getNumber() {
		return number;
	}
	
	public String toString() {
		return "Trouble" + number;
	}
}

'디자인 패턴' 카테고리의 다른 글

16. Mediator 패턴  (0) 2020.12.28
15. Facade 패턴  (0) 2020.12.26
13. Visitor 패턴  (0) 2020.12.17
12. Decorator 패턴  (0) 2020.12.16
11. Composite 패턴  (0) 2020.12.15

개요

 데이터 구조 안에 많은 요소가 저장되 있고, 각 요소를 가지고 어떤 작업을 해야할 때 그 작업은 어디어 기술해 놓아야 할까? 쉽게 생각한다면 요소 자체에 저장해놓겠지만, 작업이 다양해 진다면 요소마다 새로운 작업을 추가해야 할 것이다.  Visitor 패턴은 작업(=처리)를 요소와 분리하기 위한 패턴으로 특정한 작업(=처리)이 기술된 방문자라는 역할을 만들어서 테이터 구조를 이루는 요소를 적절한 방문자로 처리하는 기법이다. 요소들은 처리방법에 대해 몰라도 되고 방문자를 받아들여서 작업을 하면 상세한 처리 방법은 받아들인 방문자가 가지고 있는 것이다.

 

 

역할

Visitor 역할

  데이터 구조를 이루는 구체적요소(ConcreteElment)를 가지고 실행하는 visit()  메소드를 선언한다. 예제에서는 Visitor 클래스.

 

ConcreteVisitor 역할

  Visiotr역할에서 선언한 visit() 메소드를 각각 필요한 역할에 따라 구현한다. 예제에서는 ListVisitor 클래스.

 

Element 역할

  Visitor가 방문할 대상이 되는 역할로, Visitor를 받아들이는 accept() 메서드를 선언한다. 예제에서는 Element 인터페이스.

 

ConcreteElement 역할

  Element 역할의 인터페이스를 구현하는 역할 예제에서는 File클래스나 Directory 클래스.

 

ObjectStructure 역할

  Element의 집합을 취급하는 역할. 예제에서는 Directory 클래스가 ConcreteElment역할과 함께 ObjectStructure 역할을 한다.

 

이점 및 유의사항

 Visitor패턴의 가장 큰 특징은 바로 데이터 구조와 처리를 분리하는 것이다. 데이터의 구조는 요소를 정리하거나 요소간을 연결해주는 역할이라면 처리는 데이터 구조를 이용한 어떤 작업 과정에 대한 역할로 분리된다.

 결과적으로 ConcreteElement역할을 하는 (예제에서 File, Directory 클래스) 대상을 처리 과정에서 특정한 부품으로 독립시킬 수 있다. 이를 통해 새로운 처리과정이 필요하거나 수정해야 할 경우 ConcreteElement는 손대지 않고도 수정이 가능한 것이다.

 

 

예제 코드

//Visitor 역할
package visitor2;

public abstract class Visitor {
	public abstract void visit(File file);
	public abstract void visit(Directory directory);
}


//ConcreteVisitor 역할
package visitor2;

import java.util.Iterator;

public class ListVisitor extends Visitor{
	private String currentdir = "";
	public void visit(File file) {
		System.out.println(currentdir + "/" + file);
	}
	
	public void visit(Directory directory) {
		System.out.println(currentdir + "/" + directory);
		String savedir = currentdir;
		currentdir = currentdir + "/" + directory.getName();
		Iterator<Entry> it = directory.iterator();
		
		while(it.hasNext()) {
			Entry entry = (Entry)it.next();
			entry.accept(this);
		}
		currentdir = savedir;
	}
	
	
	
}


//Element 역할1
package visitor2;

public interface Element {
	public abstract void accept(Visitor v);
}


//Element 역할2
package visitor2;

import java.util.Iterator;

public abstract class Entry implements Element{
	public abstract String getName();
	public abstract int getSize();
	
	public Entry add(Entry entry) throws FileTreatmentException {
		throw new FileTreatmentException();
	}
	
	public Iterator<Entry> iterator() throws FileTreatmentException {
		throw new FileTreatmentException();
	}
	
	public String toString() {
		return getName() + " (" + getSize() + ")";
	}
}


//ConcreteElement 역할1
package visitor2;

public class File extends Entry{
	private String name;
	private int size;
	
	public File(String name, int size) {
		this.name = name;
		this.size = size;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		return size;
	}
	
	public void accept(Visitor v) {
		v.visit(this);
	}
}


//ConcreteElement 역할2
//ObjectStructure 역할
package visitor2;

import java.util.ArrayList;
import java.util.Iterator;

public class Directory extends Entry{
	private String name;
	private ArrayList<Entry> dir = new ArrayList<>();
	
	public Directory(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		int size = 0;
		Iterator<Entry> it = dir.iterator();
		
		while(it.hasNext()) {
			Entry entry = (Entry)it.next();
			size += entry.getSize();
		}
		return size;
	}
	
	
	public Entry add(Entry entry) {
		dir.add(entry);
		return this;
	}
	
	public Iterator<Entry> iterator() {
		return dir.iterator();
	}
	
	public void accept(Visitor v) {
		v.visit(this);
	}
	
}


//Tester
package visitor2;

public class VisitorTester {
	public static void main(String[] args) {
		try {
			System.out.println("Making root entries");
			Directory rootdir = new Directory("root");
			Directory bindir = new Directory("bin");
			Directory tmpdir = new Directory("tmp");
			Directory usrdir = new Directory("usr");
			
			rootdir.add(bindir);
			rootdir.add(tmpdir);
			rootdir.add(usrdir);
			bindir.add(new File("vi", 10000));
			bindir.add(new File("latex", 20000));
			rootdir.accept(new ListVisitor());
			
			System.out.println("");
			System.out.println("Making user entries..");
			Directory Kim = new Directory("Kim");
			Directory Lee = new Directory("Lee");
			Directory Park = new Directory("Park");
			
			usrdir.add(Kim);
			usrdir.add(Lee);
			usrdir.add(Park);
			Kim.add(new File("diart.html", 100));
			Kim.add(new File("Composite.java", 200));
			Lee.add(new File("memo.txt", 300));
			Park.add(new File("game.doc", 400));
			Park.add(new File("junk", 500));
			rootdir.accept(new ListVisitor());
			
		} catch(FileTreatmentException e) {
			e.printStackTrace();
		}
	}
}


//기타
package visitor2;

public class FileTreatmentException extends RuntimeException{
	public FileTreatmentException() {
		
	}
	
	public FileTreatmentException(String msg) {
		super(msg);
	}
}

'디자인 패턴' 카테고리의 다른 글

15. Facade 패턴  (0) 2020.12.26
14. Chain of Responsibility 패턴  (0) 2020.12.22
12. Decorator 패턴  (0) 2020.12.16
11. Composite 패턴  (0) 2020.12.15
10. Strategy 패턴  (0) 2020.12.09

개요

 Decorator패턴은 이름 그대로 동일한 내용물을 어떻게 장식할 것인가 하는 문제를 다루는 패턴이다. 같은 내용물을 가지고 여러가지 꾸미기 옵션 중 원하는 옵션을 선택함으로써 결과물이 달라진다. 여기서 중요한 점은 인터페이스는 모두 동일하기 때문에 어떻게 꾸밀지 선택할 때 방법을 고민하지 않아도 되는 것이다. 또한 원하는 장식을 여러번 반복 사용하여 자유롭게 꾸밀 수 있다.

 대표적인 경우가 java.io 패키지의 클래스들인데, 실제 코드를 작성하면서 Reader아래에 FileReader, BufferedReader, LineNumberReader 등 다양한 클래스가 있고 대상(자료)를 필요한 클래스를 이용해서 여러번 감싸(장식)해서 사용하게 된다.

 

 

역할

Component 역할

  핵심적인 기능이 정의하는 역할로 인터페이스를 결정한다. 예제에서는 Display 클래스.

 

ConcreteComponent 역할

  Component 역할에서 정의한 인터페이스에 따라 기능을 구현한다. 예제에서는 StringDisplay 클래스.

 

Decorator 역할

  Component와 동일한 인터페이스를 갖고, 자신이 장식할 Component 대상을 가지고 있다. 예제에서는 Border 클래스.

 

ConcreteDecorator 역할

  Decorator를 구현한다. 예제에서는 SideBorder클래스와 FullBorder 클래스.

 

 

장점 및 유의사항

 구체적 내용물(ConcreteComponet)과 장식(Decorator)은 동일한 조상(Component)을 갖기에 동일한 인터페이스를 보유한다. 또한 장식에서 저장하는 내용물은 Component 타입으로 저장하기에 구체적인 내용물(ConcreteComponent)이 어떤 것이 오든 저장할 수 있어야 한다.

 구조적으로 장식과 내용물을 동일하게(Component로) 보기 때문에 내용물을 장식으로 어떻게 감싸던 인터페이스가 동일하게 된다. 이러한 특징을 인터페이스가 투과적이다 표현하는데 장식을 여러번 감싸더라도 동일한 인터페이스로 사용할 수 있다. 예제와 같이 여러번 장식을 감싸면서 장식을 반복하다가 내용물이 있는 구조는 Composite 패턴과 같이 구조적으로 재귀적인 특징을 보여준다.

  장식을 통해서 내용물을 변경하지 않고도 새로운 기능을 동적으로 얼마든지 추가해 줄 수 있다는 장점이 있는데, 반대로 보면 장식이 다양해지게 되면서 유사한 성질의 장식(클래스)들이 지나치게 많아질 수 있기에 주의해야 한다.

 

 

예제 코드

//Component 역할
package decorator;

public abstract class Display {
	public abstract int getColumns();
	public abstract int getRows();
	public abstract String getRowText(int row);
	public final void show() {
		for(int i=0; i<getRows(); i++) {
			System.out.println(getRowText(i));
		}
	}
}


//ConcreteComponent 역할
package decorator;

public class StringDisplay extends Display{
	private String string;
	
	public StringDisplay(String string) {
		this.string = string;
	}
	
	public int getColumns() {
		return string.getBytes().length;
	}
	
	public int getRows() {
		return 1;
	}
	
	public String getRowText(int row) {
		if(row==0)
			return string;
		else
			return null;
	}
}


//Decorator 역할
package decorator;

public abstract class Border extends Display{
	protected Display display;
	
	protected Border(Display display) {
		this.display = display;
	}
}


//ConcreteDecorator 역할1
package decorator;

public class SideBorder extends Border{
	private char borderChar;
	public SideBorder(Display display, char ch) {
		super(display);
		this.borderChar = ch;
	}
	
	public int getColumns() {
		return 1 + display.getColumns() + 1;
	}
	
	public int getRows() {
		return display.getRows();
	}
	
	public String getRowText(int row) {
		return borderChar + display.getRowText(row) + borderChar;
	}
	
	
}


//ConcreteDecorator 역할2
package decorator;

public class FullBorder extends Border{
	public FullBorder(Display display) {
		super(display);
	}
	
	public int getColumns() {
		return 1 + display.getColumns() + 1;
	}
	
	public int getRows() {
		return 1 + display.getRows() + 1;
	}
	
	public String getRowText(int row) {
		if(row == 0) {
			return "+" + makeLine('-', display.getColumns()) + "+";
		} else if(row == display.getRows() + 1) {
			return "+" + makeLine('-', display.getColumns()) + "+";
		} else {
			return "|" + display.getRowText(row-1) + "|";
		}
	}
	
	private String makeLine(char ch, int count) {
		StringBuffer buffer = new StringBuffer();
		for(int i=0; i<count; i++) {
			buffer.append(ch);
		}
		
		return buffer.toString();
	}
}

'디자인 패턴' 카테고리의 다른 글

14. Chain of Responsibility 패턴  (0) 2020.12.22
13. Visitor 패턴  (0) 2020.12.17
11. Composite 패턴  (0) 2020.12.15
10. Strategy 패턴  (0) 2020.12.09
9. Bridge 패턴  (0) 2020.12.08

개요

 예를 들어 어떤 상자 안에는 더 작은 상자나 제품이 들어있다. 그리고 작은 상자안에도 더 작은 상자나 제품이 들어있을 수 있는데, 이렇게 반복적인 재귀구조에 적용하는 패턴이 바로 Composite 패턴이다.

 먼저 제품 or 상자 이 두 가지를 구분하지 않고 상장안에 뭔가가 있는 지를 확인한다. 뭔가가 들어 있다고 확인이 되면 다음으로 그게 상자인지, 제품인지 구분한다. 이와같은 대표적인 구조는 컴퓨터를 다루면서 이미 익숙해진 윈도즈의 폴더와 파일 구조에서 볼 수 있다. 폴더(리눅스라면 디렉터리)안에 아무것도 없다면 빈 폴더가 되지만 뭔가가 있다면 빈폴더가 아니면서 안에 있는 것이 폴더인지 파일이지 다시 구분하는 구조이다.

 

역할

Leaf 역할

  내용물을 표시하는 역할로 내부에 다른 것은 포함하지 않는다. 예제에서 File클래스.

 

Composite 역할

  상자를 나타내는 역할로 Leaf나 Composite 역할을 넣을 수 있다. 예제에서 Directory 클래스.

 

Component 역할

  Leaf나 Composite 역할의 대상을 동일시 하는 역할. 상위 클래스로 구현하며 예제에서는 Entry 클래스.

 

Client 역할

  Composite 패턴 사용자. 예제에서는 CompositeTester 클래스.

 

 

이득 및 유의사항

 Composite패턴을 사용하면 담는 박스와 내용물을 동일시 하며 또한 복수와 단수도 동일하게 취급한다. 단일 객체나 여러 개체를 다룰 경우에 차이를 두지 않고 처리가 가능한다. 트리형 자료구조가 대부분 Composite패턴에 해당하는데, 상위 하위 노드가 연결된 재귀적인 구조와 노드 안에 데이터가 저장형식이 그러하다.

 

예제 코드

//Leaf 역할
package composite;

public class File extends Entry{
	private String name;
	private int size;
	
	public File(String name, int size) {
		this.name = name;
		this.size = size;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		return size;
	}
	
	protected void printList(String prefix) {
		System.out.println(prefix +"/"+ this);
	}
}


//Composite 역할
package composite;

import java.util.ArrayList;
import java.util.Iterator;

public class Directory extends Entry{
	private String name;
	private ArrayList<Entry> directory = new ArrayList<>();
	
	public Directory(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	public int getSize() {
		int size = 0;
		Iterator<Entry> it = directory.iterator();
		while(it.hasNext()) {
			Entry entry = it.next();
			size += entry.getSize();
		}
		return size;
	}
	
	public Entry add(Entry entry) {
		directory.add(entry);
		return this;
	}
	
	protected void printList(String prefix) {
		System.out.println(prefix+"/"+this);
		Iterator<Entry> it = directory.iterator();
		
		while(it.hasNext()) {
			Entry entry = it.next();
			entry.printList(prefix+"/"+name);
		}
	}
}


//Component 역할
package composite;

public abstract class Entry {
	public abstract String getName();
	public abstract int getSize();
	public Entry add(Entry entry) throws FileTreatmentException{
		throw new FileTreatmentException();
	}
	
	public void printList() {
		printList("");
	}
	
	protected abstract void printList(String prefix);
	
	public String toString() {
		return getName() + " (" + getSize() + ")";
	}
}


//Client 역할
package composite;

public class CompositeTester {
	public static void main(String[] args) {
		try {
            System.out.println("Making root entries...");
            Directory rootdir = new Directory("root");
            Directory bindir = new Directory("bin");
            Directory tmpdir = new Directory("tmp");
            Directory usrdir = new Directory("usr");
            rootdir.add(bindir);
            rootdir.add(tmpdir);
            rootdir.add(usrdir);
            bindir.add(new File("vi", 10000));
            bindir.add(new File("latex", 20000));
            rootdir.printList();

            System.out.println("");
            System.out.println("Making user entries...");
            Directory yuki = new Directory("yuki");
            Directory hanako = new Directory("hanako");
            Directory tomura = new Directory("tomura");
            usrdir.add(yuki);
            usrdir.add(hanako);
            usrdir.add(tomura);
            yuki.add(new File("diary.html", 100));
            yuki.add(new File("Composite.java", 200));
            hanako.add(new File("memo.tex", 300));
            tomura.add(new File("game.doc", 400));
            tomura.add(new File("junk.mail", 500));
            rootdir.printList();
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
		
	}
}

'디자인 패턴' 카테고리의 다른 글

13. Visitor 패턴  (0) 2020.12.17
12. Decorator 패턴  (0) 2020.12.16
10. Strategy 패턴  (0) 2020.12.09
9. Bridge 패턴  (0) 2020.12.08
8. AbstractFactory 패턴  (0) 2020.12.07

+ Recent posts