반복자 패턴(Iterator Pattern)

  반복자 패턴 이터레이터 패턴이라고도 하고 Cursor라고도 한다.

즉, 대상으로 삼는 Context는 객체를 여러개 담고 있는 Aggregate Object(Collection Obj)이다.(ex : ArrayList)

 

이런 각각의 Aggregate Objelement를 방문을 원할 때 반복자 패턴을 이용한다.

하지만 Aggregate Obj의 구현 외부에는 노출되면 안된다는 전제가 있다.

 

일반적인 ArrayList나 Built-in Array를 쓰면 매 수정마다 Client 프로그램 수정이 계속 필요하다.

이를 개성하기 위해 Iterator 인터페이스를 하나만 구현한다면

클라이언트에서는 그에 맞게만 작성하면 되는 이점이 있다.

 

다시 말하자면 컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식이 통일되어 있으면 어떤 종류의 집합체에 대해서도 사용할 수 있는 다형적인 코드를 만들수 있다.

이터레이터 패턴을 사용하면 모든 항목에 일일이 접근하는 작업을 컬렉션 객체가 아닌 반복자 객체에서 맡게 된다

이렇게 하면 집합체의 인터페이스 및 구현이 간단해질 뿐 아니라, 집합체에서는 반복작업에서 손을 떼고 원래 자신이 할 일(객체 컬렉션 관리)에만 전념할 수 있다.

반복자 패턴에서 중요하게 여겨지는 디자인 원칙 SRP(Single Responsibility Principle)이다.

클래스는 단 하나만의 변경이 되어야하고 여러개면 분해해야 한다.(클래스 단일 책임 원칙)

 

반복자 패턴 클래스 다이어 그램

반복자 패턴 예제

 Aggregate(골재) 인터페이스 : 추상 메서드를 하나 가지고 있다.

package com.patterns.IteratorPattern;

public interface Aggregate {
	public abstract Iterator createIterator();
}

Iterator 인터페이스의  위의 클래스 다이어그램에 있는 3가지 메서드를 가진다.

package com.patterns.IteratorPattern;

public interface Iterator {
	
	public boolean hasNext();
	public Object next();
	public void remove();
}

CoffeeMenuIterator 클래스 : 위의 내용을 구현해두고 있다.

package com.patterns.IteratorPattern;

public class CoffeeMenuIterator implements Iterator {

	private CoffeeMenu coffeeMenu;
	private int index;

	public CoffeeMenuIterator(CoffeeMenu coffeeMenu) {
		this.coffeeMenu = coffeeMenu;
		this.index = 0;
	}

	@Override
	public boolean hasNext() {
		return index < coffeeMenu.getLength();
	}

	@Override
	public Object next() {
		Coffee coffee = coffeeMenu.getCoffee(index);
		index++;
		return coffee;
	}

	@Override
	public void remove() {
		coffeeMenu.removeCoffee(index);
	}

}

 

Coffee 클래스 :  Coffee에 대한 정보를 가진다.

package com.patterns.IteratorPattern;

public class Coffee {
	private String name;
	private int cost;

	public Coffee(String name, int cost) {
		this.name = name;
		this.cost = cost;
	}

	public String getName() {
		return name;
	}

	public int getCost() {
		return cost;
	}
}

CoffeeMenu 클래스

Coffee의 객체를 배열로 만들어 이용한다.

이때 CoffeeMenuIterator createIterator()을 통해 현재 객체를 보냄으로써 이터레이터 객체를 생성 할 수 있다.

package com.patterns.IteratorPattern;

public class CoffeeMenu implements Aggregate {
	private Coffee[] coffees;
	private int size = 0;

	public CoffeeMenu(int size) {
		coffees = new Coffee[size];
	}

	public Coffee getCoffee(int index) {
		return coffees[index];
	}

	public void addCoffee(Coffee coffee) {
		coffees[size] = coffee;
		size++;
	}

	public int getLength() {
		return size;
	}

	public void removeCoffee(int index) {
		if (index + 1 == size) {
			return;
		}
		for (int i = index; i < size - 1; i++) {
			coffees[i] = coffees[i + 1];
		}
		size--;
	}

	public CoffeeMenuIterator createIterator() {
		return new CoffeeMenuIterator(this);
	}
}

CoffeeTest 클래스

package com.patterns.IteratorPattern;

public class CoffeeTest {
	public static void main(String[] args) {
		CoffeeMenu coffeeMenu = new CoffeeMenu(3);

		coffeeMenu.addCoffee(new Coffee("아이스 아메리카노", 3000));
		coffeeMenu.addCoffee(new Coffee("아이스 라떼", 4000));
		coffeeMenu.addCoffee(new Coffee("더치 블랙", 3800));

		CoffeeMenuIterator iterator = coffeeMenu.createIterator();

		while (iterator.hasNext()) {
			Coffee coffee = (Coffee) iterator.next();
			System.out.println("커피 이름 : " + coffee.getName());
			System.out.println("커피 가격 : " + coffee.getCost());
		}

		System.out.println("=== Remove call ===");
		iterator = coffeeMenu.createIterator();
		iterator.remove();

		while (iterator.hasNext()) {
			Coffee coffee = (Coffee) iterator.next();
			System.out.println("커피 이름 : " + coffee.getName());
			System.out.println("커피 가격 : " + coffee.getCost());
		}
	}
}

 

커피 이름 : 아이스 아메리카노
커피 가격 : 3000
커피 이름 : 아이스 라떼
커피 가격 : 4000
커피 이름 : 더치 블랙
커피 가격 : 3800
=== Remove call ===
커피 이름 : 아이스 라떼
커피 가격 : 4000
커피 이름 : 더치 블랙
커피 가격 : 3800

 

정리

컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 제공한다.
Iterator 패턴은 일련의 순서를 가진 데이터 집합에 대하여 순차적인 접근을 지원하는 패턴이다.

단일 책임 원칙 (SRP: Single Responsibility Principle) 을 따른다.

 

코드를 변경할 만한 이유가 두가지가 되면 그만큼 그 클래스를 나중에 고쳐야 할 가능성이 커지게 될 뿐 아니라, 

디자인에 있어서 두 가지 부분이 동시에 영향이 미치게 된다.

따라서 SRP 원칙에 따르면 한 역할은 한 클래스에서만 맡게 해야 한다.

 

참고원문: https://www.crocus.co.kr/1532?category=337544

 

반복자 패턴(Iterator Pattern, Cursor)

반복자 패턴(Iterator Pattern) 반복자 패턴은 이터레이터 패턴이라고도 하고 Cursor라고도 한다. 즉, 대상으로 삼는 Context는 객체를 여러개 담고 있는 Aggregate Object(Collection Obj)이다.(ex : ArrayList..

www.crocus.co.kr

 

옵저버 패턴(Observer Pattern)

  한 객체의 상태가바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다. 

 즉, A 오브젝트가 B 오브젝트 상태에 관심이 있어서 B 오브젝트 상태가 변할때 마다 A 오브젝트가 파악할 수 있게 해주는 패턴이다.

 

신문 구독이 대표적인 예이다.(신문 발행자, 구독자 관계)

 

옵저버 패턴의 특징은 Loose Coupling을 잘 만족하여 서로 아는 지식은 적지만, 커뮤니케이션은 가능하다는 점이다.

즉, 사용되는 디자인 원칙은 다음과 같다.

 

서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

 

옵저버 패턴 클래스 다이어그램

 

 

옵저버 패턴 예제

Observer 인터페이스 정의

인터페이스 내에 update 메서드를 정의한다.

package com.patterns.ObserverPattern;

public interface Observer {
	public void update(String title, String news);
}

 

Publisher 인터페이스 정의

인터페이스 내에 아래 3가지 메서드를 정의하여 publisher이 행동 할 수 있는 것들을 명시한다.

package com.patterns.ObserverPattern;

public interface Publisher {
	
	public void registerObserver(Observer observer);
	public void removeObserver(Observer observer);
	public void notifyObservers();
}

NewsPublisher 클래스 구현

   Publisher 인터페이스를 직접 이용하는 클래스

 

   observers를 ArrayList에 모을 수 있게 한다.

   그 외에는 아래 코드를 참고한다.

package com.patterns.ObserverPattern;

import java.util.ArrayList;

public class NewsPublisher implements Publisher {
	
	private ArrayList<Observer> observers;
	private String title;
	private String news;

	public NewsPublisher() {
	    observers = new ArrayList<>();
	    title = null;
	    news = null;
	  }

	@Override
	public void registerObserver(Observer observer) {
		observers.add(observer);
	}

	@Override
	public void removeObserver(Observer observer) {
		int index = observers.indexOf(observer);
		observers.remove(index);
	}

	@Override
	public void notifyObservers() {
		for (Observer observer : observers) {
			observer.update(title, news);
		}
	}

	public void setNews(String title, String news) {
		this.title = title;
		this.news = news;
		notifyObservers();
	}
}

 

NewsSubscriber 클래스 구현

   Observer 인터페이스를 직접 이용하는 클래스 

 

   NewsSubscriber이 생성될 때 자신의 publisher이 누구인지 지정해준다.

   그때 자신의 publisher의 registerObserver에 의해 등록이 될 것이고

   추후 publisher의 notifyObservers()에서 publisher의 모든 observer가 내용을 전달받게 된다.

package com.patterns.ObserverPattern;

public class NewsSubscriber implements Observer {

	private String observerName;
	private String news;
	private Publisher publisher;

	public NewsSubscriber(String subscriber, Publisher publisher) {
	    this.observerName = subscriber;
	    this.publisher = publisher;
	    publisher.registerObserver(this);
	  }

	@Override
	public void update(String title, String news) {
		this.news = title + "!!! " + news;
		display();
	}

	private void display() {
		System.out.println("=== " + observerName + " 수신 내용 ===\n" + news + "\n");
	}

}

 

ObserverPatternTest 클래스 구현

package com.patterns.ObserverPattern;

public class ObserverPatternTest {
	public static void main(String[] args) {
		
		PublisherImpl newsPublisher = new PublisherImpl();

		SubscriberImpl newsSubscriber1 = new SubscriberImpl("옵저버1", newsPublisher);
		SubscriberImpl newsSubscriber2 = new SubscriberImpl("옵저버2", newsPublisher);

		newsPublisher.setNews("특보", "옵저버 패턴이 만들어졌습니다.");
		newsPublisher.setNews("정정", "옵저버 패턴으로 내용이 정정됨을 알립니다.");

		newsPublisher.removeObserver(newsSubscriber1);
		newsPublisher.setNews("속보", "누군가 구독 해제를 했습니다.");
	}
}

 

 

정리

옵저버 패턴은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고 자동으로 정보가 갱신되는 1:N 의 관계를 정의한다.


옵저버 패턴은 주제와 옵저버가 느슨하게 결합되어있는 객체 디자인을 제공한다.

 

주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스(Observer 인터페이스)를 구현 한다는 것 뿐. 
옵저버는 언제든지 새로 추가할 수 있음. (주제는 Observer인터페이스 구현하는 객체 목록에만 의존하기때문)


주제나 옵저버가 바뀌더라도 서로에게 전혀 영향을 주지않는다. 

그래서 주제와 옵저버는 서로 독립적으로 재 사용할수 있다. 

느슨하게 결합하는 디자인을 사용하면 변경 사항이 생겨도 무난히 처리할 수 있는 유연한 객체지향 시스템을 구축할수 있다. (객체 사이의 상호 의존성을 최소화 할 수 있기 때문) 

새로운 형식의 옵저버를 추가하려해도 주제를 전혀 변경할 필요가 없음. (새로운 클래스에서 Observer 인터페이스만 구현해주면됨) 

옵저버 패턴은 푸시 방식과 풀 방식으로 언제든지 구현할 수 있다.

JAVA에서 기본으로 Observable 클래스와 Observer 인터페이스를 제공한다.


Swing, Android 등에서 UI관련된 곳에서 이 옵저버 패턴이 많이 사용된다.

참고 원문: https://www.crocus.co.kr/1530?category=337544 

 

옵저버 패턴(Observer Pattern)

옵저버 패턴(Observer Pattern) 한객체의 상태가바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다. 즉, A

www.crocus.co.kr

 

템플릿 메소드 패턴(Template Method Pattern)

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

 

 

 

 

 

템플릿 메서드 패턴(Template Method Pattern)


어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴이다.


즉, 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화 할 때 유용하다. 
다른 관점에서 보면 동일한 기능을 상위 클래스에서 정의하면서 확장/변화가 필요한 부분만 서브 클래스에서 구현할 수 있도록 한다. 

 

좀더 이야기 해보자면 알고리즘에 대한 Framework 정의를 통해 Subclass의 다양한 method calls를 이룬다.

실질적인 behavior은 하위 클래스에서 implement 하도록 만든다.

 

상위 클래스에서는 공통적, 하위 클래스에서는 다양하게 코딩을 한다.

즉, 확장적이고 공통적인건 상위 클래스에서 정의하고 그렇지 않은 것은 imterface로 하위 클래스에서 정의한다.

 

즉, Template method는 각 스텝이 method의 call로 진행된다.

(알고리즘의 큰 step를 정해주고 부가적인 것은 subclass가 해결하도록 하는 것이다.)

 

템플릿 메서드 패턴의 특징 하나의 클래스가 전체 알고리즘은 보호하고 진행 할 수 있다.(Framework 생성과 유사)

재사용을 통해 코드 중복을 없애고 새로운 객체가 나타나도 그에 특화된 것만 코딩을 하면 되는 특징이 있다.

 

템플릿 메서드 패턴(Template Method Pattern)

 

AbstractClass
  템플릿 메서드를 정의하는 클래스

  하위 클래스에 공통 알고리즘을 정의하고 하위 클래스에서 구현될 기능을 primitive 메서드 등으로 정의하는 클래스이다.


ConcreteClass
  물려받은 primitive 메서드 또는 hook 메서드를 구현하는 클래스

 

상위 클래스에 구현된 템플릿 메서드의 일반적인 알고리즘에서 하위 클래스에 적합하게 primitive 메서드 등을 오버라이드 하는 클래스이다.

 

템플릿 메서드 패턴 예제

아이스 아메리카노와 아이스 라떼를 만드는 법을 비교해보자.

(지극히 제 생각으로 만드는 것이고 일반 카페에서 만드는 방법과 다를 수 있습니다.)

 

아이스 아메리카노 아이스 라떼
1. 물을 끓인다.
2. 끓는 물에 에스프레소를 넣는다.
3. 얼음을 넣는다.
4. 시럽을 넣는다.
1. 물을 끓인다.
2. 끓는 물에 에스프레소를 넣는다
3. 얼음을 넣는다
4, 우유를 넣는다.

ㅁ 일반코딩 방법

IceAmericano.java

package com.patterns.TemplateMethodPattern.Before;

public class IceAmericano {
	public void makeIceAmericano() {
		boilWater();
		putEspresso();
		putIce();
		putSyrup();
	}

	private void boilWater() {
		System.out.println("물을 끓인다.");
	}

	private void putEspresso() {
		System.out.println("끓는 물에 에스프레소를 넣는다.");
	}

	private void putIce() {
		System.out.println("얼음을 넣는다.");
	}

	private void putSyrup() {
		System.out.println("시럽을 넣는다.");
	}
}

IceLatte.java

package com.patterns.TemplateMethodPattern.Before;

public class IceLatte {
	public void makeIceLatte() {
		boilWater();
		putEspresso();
		putIce();
		putMilk();
	}

	private void boilWater() {
		System.out.println("물을 끓인다.");
	}

	private void putEspresso() {
		System.out.println("끓는 물에 에스프레소를 넣는다.");
	}

	private void putIce() {
		System.out.println("얼음을 넣는다.");
	}

	private void putMilk() {
		System.out.println("우유를 넣는다.");
	}

}

NoTemplateMethodPattern.java 

package com.patterns.TemplateMethodPattern.Before;

public class NoTemplateMethodPattern {
	public static void main(String[] args) {
		
		IceAmericano americano = new IceAmericano();
		IceLatte latte = new IceLatte();

		americano.makeIceAmericano();
		System.out.println("===");
		latte.makeIceLatte();
	}
}

 

그런데 위의 코드는 중복되는 코드가 많다.

따라서 한번 추상화 시켜 클래스 다이어그램으로 나타내보자.

 

1단계 추상화  추가 추상화

 

무언가 Coffee 클래스에 공통된 것을 두고, 상속을 통해 자식 클래스에서 마무리하면 될 것 같다.

하지만 여기서 makeCoffee를 한단계 더 추상화 할 수 있다.

하지만 여기서 한단계 더 추상화 할 수 있다.

ㅁ 템플릿 메소드 패턴코딩 방법

Coffee.java

package com.patterns.TemplateMethodPattern;

public abstract class Coffee {
	final void makeCoffee() {
		boilWater();
		putEspresso();
		putIce();
		putExtra();
	}

	// SubClass에게 확장/변화가 필요한 코드만 코딩하도록 한다.
	abstract void putExtra();

	// 공통된 부분은 상위 클래스에서 해결하여 코드 중복을 최소화 시킨다.
	private void boilWater() {
		System.out.println("물을 끓인다.");
	}

	private void putEspresso() {
		System.out.println("끓는 물에 에스프레소를 넣는다.");
	}

	private void putIce() {
		System.out.println("얼음을 넣는다.");
	}
}

IceAmericano.java

package com.patterns.TemplateMethodPattern;

public class IceAmericano extends Coffee {
	@Override
	void putExtra() {
		System.out.println("시럽을 넣는다.");
	}
}

IceLatte.java

package com.patterns.TemplateMethodPattern;

public class IceLatte extends Coffee {
	@Override
	void putExtra() {
		System.out.println("우유를 넣는다.");
	}
}

 

CoffeeTest.java : 테스트 

package com.patterns.TemplateMethodPattern;

public class CoffeeTest {
	public static void main(String[] args) {
		
		IceAmericano americano = new IceAmericano();
		IceLatte latte = new IceLatte();

		americano.makeCoffee();
		System.out.println("===");
		latte.makeCoffee();
	}

}

 

정리

 

템플릿 메소드 패턴은 "알고리즘의 뼈대"를 맞추는 것에 있다. 

즉, 전체적인 레이아웃을 통일 시키지만 상속받은 클래스로 하여금 어느정도 유연성을 주도록하는 디자인 패턴이다. 

추상 메소드(abstrcat method)와 훅 메소드(hook method)를 적절히 사용해서 

전체적인 알고리즘의 뼈대를 유지하되 유연하게 기능을 변경할 수 있도록 하고자 할 때 사용하면 유용하다.

참고 출처: https://www.crocus.co.kr/1531?category=337544 

 

템플릿 메서드 패턴(Template Method Pattern)

템플릿 메서드 패턴(Template Method Pattern) 어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바

www.crocus.co.kr

 

 

데코레이터 패턴(Decorator Pattern)은 Flyweight 패턴, Adapter 패턴, Bridge 패턴처럼 구조 패턴 중 하나로, 런타임에서 객체의 기능을 수정하는데 사용되는 패턴입니다.

 

데코레이터 패턴 이해 및 예제

데코레이터 패턴은 상속(Inheritance)과 합성(Composition)을 사용하여 객체에 동적으로 책임을 추가할 수 있게 합니다. 이 방법은 서브 클래스를 생성하는 것보다 유연한 방법을 제공합니다.

 

예를 들어 다양한 종류의 자동차를 구현한다고 가정해보겠습니다. Car 라는 인터페이스를 정의하고 이를 구현하는 Basic Car 를 두고, 이를 확장하여 구현하는 Sports Car, Luxury Car 를 정의해보겠습니다.

 

그림으로 나타내면 다음과 같습니다.

그런데 만약 런타임 단계에서 Sports Car의 특징과 Luxury Car의 특징을 모두 갖고 있는 Car 를 얻고 싶다면 어떻게 해야할까요? 일반적으로 둘의 기능을 갖는 또다른 서브 클래스를 정의할 수도 있겠습니다만, 데코레이터 패턴을 사용하여 해결해보도록 하겠습니다.

 

Car.java

package com.patterns.DecoratorPattern;

public interface Car {
	public void assemble();
}

 

BasicCar.java

package com.patterns.DecoratorPattern;

public class BasicCar implements Car {
	 
	@Override
	public void assemble() {
		System.out.print("Basic Car.");
	}
}

 

CarDecorator.java

package com.patterns.DecoratorPattern;

public class CarDecorator implements Car {
	 
	protected Car car;
	
	public CarDecorator(Car c){
		this.car=c;
	}
	
	@Override
	public void assemble() {
		this.car.assemble();
	}
}

 

SportsCar.java

package com.patterns.DecoratorPattern;

public class SportsCar extends CarDecorator {
	 
	public SportsCar(Car c) {
		super(c);
	}
 
	@Override
	public void assemble(){
		super.assemble();
		System.out.print(" Adding features of Sports Car.");
	}
}

 

LuxuryCar.java

package com.patterns.DecoratorPattern;

public class LuxuryCar extends CarDecorator {
	 
	public LuxuryCar(Car c) {
		super(c);
	}
	
	@Override
	public void assemble(){
		super.assemble();
		System.out.print(" Adding features of Luxury Car.");
	}
}

 

이제 모든 클래스가 준비되었으니 테스트 코드를 작성해보겠습니다.

 

DecoratorPatternTest.java

package com.patterns.DecoratorPattern;

public class DecoratorPatternTest {
	 
	public static void main(String[] args) {
		Car sportsCar = new SportsCar(new BasicCar());
		sportsCar.assemble();
		System.out.println("\n*****");
		
		Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
		sportsLuxuryCar.assemble();
	}
}
Basic Car. Adding features of Sports Car.
*****
Basic Car. Adding features of Luxury Car. Adding features of Sports Car.

보시다시피 CarDecorator 를 사용하여 SportsCar와 LuxuryCar 의 기능을 합성할 수 있게 됐습니다. 이렇게 작성할 경우 클래스의 종류가 많아질 수록 큰 효과를 발휘할 수 있는데요. 런타임에서 다양한 기능을 조합하여 기능을 사용할 수 있습니다.

 

중요 포인트

  • 데코레이터 패턴은 런타임에서 유연하게 객체의 기능들을 수정하고 조합하는데 유용하게 사용되는 패턴입니다.
  • 단점이 있다면, 다수의 데코레이터 객체를 생성하고 사용해야 한다는 것입니다.
  • JDK 에서 FileReader, BufferedReader 등 IO 클래스에 사용되는 패턴입니다.

 

참고 원문 : https://readystory.tistory.com/195?category=822867 

 

[구조 패턴] 데코레이터 패턴(Decorator Pattern) 이해 및 예제

데코레이터 패턴(Decorator Pattern)은 Flyweight 패턴, Adapter 패턴, Bridge 패턴처럼 구조 패턴 중 하나로, 런타임에서 객체의 기능을 수정하는데 사용되는 패턴입니다. 구조 패턴(Structural Pattern)이란? 구..

readystory.tistory.com

 

브릿지 패턴(Bridge Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 두 인터페이스에 계층 구조(Hierarchy)를 가지고 있을 때 인터페이스를 구현(implements)으로부터 분리하고 클라이언트 프로그램으로부터 구현 세부사항을 숨기기 위해 사용되는 패턴입니다.

 

브릿지 패턴 이해 및 예제

디자인 패턴의 교과서인 GoF에서는 브릿지 패턴에 대해 다음과 같이 정의하고 있습니다.

추상화(abstraction)를 구현으로부터 분리하여 각각 독립적으로 변화할 수 있도록 하는 패턴

 

예제를 통해 이해를 돕도록 하겠습니다.

예를 들어 다음과 같은 구조의 인터페이스를 구현하는 프로그램을 만든다고 가정해보겠습니다.

 

보시다시피 Shape 과 Color 라는 2가지 인터페이스가 있습니다.

이제 브릿지 패턴을 사용하여 두 인터페이스의 compoisition 을 구성해보도록 하겠습니다.

 

Color.java

package com.patterns.BridgePattern;

public interface Color {
	 
	public void applyColor();
}

Shape.java

package com.patterns.BridgePattern;

public abstract class Shape {
	//Composition
	protected Color color;
	
	//constructor with implementor as input argument
	public Shape(Color c){
		this.color=c;
	}
	
	abstract public void applyColor();
}

Shape 클래스가 Color 인터페이스를 소유하고 있고, applyColor() 메소드는 abstract 로 선언하여 하위 클래스에게 구현을 위임합니다.

 

이번에는 이 Shape 추상 클래스를 상속하여 구체화 하는 Triangle 클래스와 Pentagon 클래스를 정의해보겠습니다.

 

Triangle.java

package com.patterns.BridgePattern;

public class Triangle extends Shape {
	 
	public Triangle(Color c) {
		super(c);
	}
 
	@Override
	public void applyColor() {
		System.out.print("Triangle filled with color ");
		color.applyColor();
	} 
 
}

Pentagon.java

package com.patterns.BridgePattern;

public class Pentagon extends Shape {
	 
	public Pentagon(Color c) {
		super(c);
	}
 
	@Override
	public void applyColor() {
		System.out.print("Pentagon filled with color ");
		color.applyColor();
	} 
 
}

마지막으로 각 Shape 클래스가 소유할 Color 인터페이스의 구현 객체를 정의해보겠습니다. 컬러는 RedColor 와 GreenColor 로 선언하겠습니다.

 

RedColor.java

package com.patterns.BridgePattern;

public class RedColor implements Color{
	 
	public void applyColor(){
		System.out.println("red.");
	}
}

GreenColor.java

package com.patterns.BridgePattern;

public class GreenColor implements Color{
	 
	public void applyColor(){
		System.out.println("green.");
	}
}

 

이제 위에서 정의한 클래스들을 사용하여 테스트 해보도록 하겠습니다.

 

BridgePatternTest.java

package com.patterns.BridgePattern;

public class BridgePatternTest {
	 
	public static void main(String[] args) {
		Shape tri = new Triangle(new RedColor());
		tri.applyColor();
		
		Shape pent = new Pentagon(new GreenColor());
		pent.applyColor();
	}
}

 

결과

Triangle filled with color red.
Pentagon filled with color green.

 

브릿지 디자인 패턴은 추상화(abstraction)와 구현(implement)이 독립적으로 다른 계층 구조를 가질 수 있고, 클라이언트 어플리케이션으로부터 구현을 숨기고 싶을 때 사용될 수 있습니다.

 

참고 원문 : https://readystory.tistory.com/194?category=822867 

 

[구조 패턴] 브릿지 패턴(Bridge Pattern) 이해 및 예제

브릿지 패턴(Bridge Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 두 인터페이스에 계층 구조(Hierarchy)를 가지고 있을 때 인터페이스를 구현(implements)으로부터 분리하..

readystory.tistory.com

 

퍼사드 패턴(Facade Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 클라이언트가 쉽게 시스템과 상호작용 할 수 있도록 도와주는 패턴입니다.

퍼사드 패턴 이해 및 예제

디자인 패턴의 교과서인 GoF에서는 퍼사드 패턴에 대해 다음과 같이 정의하고 있습니다.

퍼사드 패턴은 서브시스템을 더 쉽게 사용할 수 있도록 higher-level 인터페이스를 정의하고, 제공합니다. 

 

이해를 돕기 위해 예시를 들어보겠습니다.

MySql 과 Oracle 데이터베이스를 사용하는 인터페이스가 있고, 이를 이용해 HTML 리포트 또는 PDF 리포트를 생성해야 한다고 가정해보도록 하겠습니다.

 

우리는 이 인터페이스들을 서로 조합하여 리포트를 만들어야 합니다. 지금은 database connection 방법 2가지, generate report 방법 2가지의 인터페이스를 가지고서 조합을 해야 하지만 만약 경우의 수가 더 복잡해질 경우 일반적인 방법으로는 관리하기가 어려워질 것입니다.

 

그래서 우리는 퍼사드 패턴을 적용하여 wrapper 인터페이스를 제공하여 클라이언트가 보다 쉽게 관리할 수 있도록 도와보겠습니다.

 

MySqlHelper.java

package com.patterns.facadepattern;

import java.sql.Connection;

public class MySqlHelper {
	
	public static Connection getMySqlDBConnection(){
		// 실제 커넥션을 리턴해야 하지만, 예제이기에 null 을 리턴하겠습니다.
		return null;
	}
	
	public void generateMySqlPDFReport(String tableName, Connection con){
		// get data from table and generate pdf report
	}
	
	public void generateMySqlHTMLReport(String tableName, Connection con){
		// get data from table and generate pdf report
	}
}

 

OracleHelper.java

package com.patterns.facadepattern;

import java.sql.Connection;

public class OracleHelper {
 
	public static Connection getOracleDBConnection(){
		// 실제 커넥션을 리턴해야 하지만, 예제이기에 null 을 리턴하겠습니다.
		return null;
	}
	
	public void generateOraclePDFReport(String tableName, Connection con){
		// get data from table and generate pdf report
	}
	
	public void generateOracleHTMLReport(String tableName, Connection con){
		// get data from table and generate pdf report
	}
	
}

 

이번에는 퍼사드 패턴 인터페이스를 생성해보겠습니다. 타입 안정성을 위해서 디비 타입과 리포트 타입에 대해서는 Enum을 사용하도록 하겠습니다.

 

HelperFacade.java

package com.patterns.facadepattern;

import java.sql.Connection;

public class HelperFacade {
 
	public static void generateReport(DBTypes dbType, ReportTypes reportType, String tableName){
		Connection con = null;
		switch (dbType){
		case MYSQL: 
			con = MySqlHelper.getMySqlDBConnection();
			MySqlHelper mySqlHelper = new MySqlHelper();
			switch(reportType){
			case HTML:
				mySqlHelper.generateMySqlHTMLReport(tableName, con);
				break;
			case PDF:
				mySqlHelper.generateMySqlPDFReport(tableName, con);
				break;
			}
			break;
		case ORACLE: 
			con = OracleHelper.getOracleDBConnection();
			OracleHelper oracleHelper = new OracleHelper();
			switch(reportType){
			case HTML:
				oracleHelper.generateOracleHTMLReport(tableName, con);
				break;
			case PDF:
				oracleHelper.generateOraclePDFReport(tableName, con);
				break;
			}
			break;
		}
		
	}
	
	public static enum DBTypes{
		MYSQL,ORACLE;
	}
	
	public static enum ReportTypes{
		HTML,PDF;
	}
}

 

위 코드가 모두 준비되었다면 이제 메인 함수를 통해 테스트해보겠습니다.

 

FacadePatternTest.java

package com.patterns.facadepattern;

public class FacadePatternTest {
 
	public static void main(String[] args) {
		String tableName="Employee";
		
		//generating MySql HTML report and Oracle PDF report using Facade
		HelperFacade.generateReport(HelperFacade.DBTypes.MYSQL, HelperFacade.ReportTypes.HTML, tableName);
		HelperFacade.generateReport(HelperFacade.DBTypes.ORACLE, HelperFacade.ReportTypes.PDF, tableName);
	}
}

 

시다시피 퍼사드 패턴을 사용하게 되면 클라이언트 사이드에서 무겁게 로직을 작성할 필요 없이 쉽고 깔끔하게 리포트를 생성할 수 있게 됩니다.

 

퍼사드 패턴의 중요 포인트!

  • 퍼사드 패턴은 클라이언트 어플리케이션의 헬퍼 역할을 하는 것이지, 서브시스템 인터페이스를 숨기는 것은 아닙니다.
  • 퍼사드 패턴은 특정 기능에 대해 인터페이스의 수가 확장되고(위 예제로 치면 디비 종류나 리포트 종류가 늘어난다는 등), 시스템이 복잡해질 수 있는 상황에서 사용하기 적합합니다.
  • 퍼사드 패턴은 비슷한 작업을 해야하는 다양한 인터페이스들 중 하나의 인터페이스를 클라이언트에 제공해야 할 때 적용하는 것이 좋습니다.
  • 팩토리 패턴과 종종 함께 사용됩니다.

참조원문 : https://readystory.tistory.com/193?category=822867

 

플라이웨이트 패턴(Flyweight Pattern)은 Facade 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 많은 수의 객체를 생성해야 할 때 사용하는 패턴입니다.

플라이웨이트 패턴 이해 및 예제

디자인 패턴의 교과서인 GoF에서는 플라이웨이트 패턴에 대해 다음과 같이 정의하고 있습니다.

'공유(Sharing)'를 통하여 대량의 객체들을 효과적으로 지원하는 방법

이처럼 플라이웨이트 패턴은 많은 수의 객체를 생성해야 할 때 주로 쓰입니다.

 

모든 객체는 저마다의 메모리를 할당받습니다. 따라서 모바일 기기나 임베디드 시스템 등 저용량 메모리를 지원하는 기기에서는 메모리를 관리하는 것이 아주 중요합니다.

 

플라이웨이트 패턴은 공유 객체에 의해 메모리에 로드 되는 객체의 개수를 줄일 수 있습니다.

 

플라이웨이트를 적용하기에 앞서 몇 가지 확인할 것이 있습니다.

아래 중 해당하는 사항이 많은 상황일수록 플라이웨이트 패턴을 적용하기에 적합합니다.

  • 어플리케이션에 의해 생성되는 객체의 수가 많아야 한다.
  • 생성된 객체가 오래도록 메모리에 상주하며, 사용되는 횟수가 많다.
  • 객체의 특성을 내적 속성(Intrinsic Properties)과 외적 속성(Extrinsic Properties)으로 나눴을 때, 객체의 외적 특성이 클라이언트 프로그램으로부터 정의되어야 한다.

다른 건 어느 정도 알겠는데, 객체의 내적 속성과 외적 속성이라니 생소하게 느껴지셨을 겁니다.

하지만 플라이웨이트 패턴을 적용하기 위해서는 객체의 속성을 내적 속성과 외적 속성으로 나누어야 합니다.

그렇다면 내적 속성과 외적 속성은 무엇일까요?

 

객체의 내적 속성은 객체를 유니크하게 하는 것이고, 외적 속성은 클라이언트의 코드로부터 설정되어 다른 동작을 수행하도록 사용되는 특성입니다. 예를 들어, Circle 이라는 객체는 color와 width라는 외적 속성을 가질 수 있습니다.

 

자, 이제 플라이웨이트 패턴의 예제를 살펴볼 텐데, 그러기 위해서 우리는 Shared Objects를 리턴하는 Flyweight Factory를 생성해야 합니다. 이번 예제에서는 Line과 Oval을 통해 도형을 그리는 코드를 작성해보도록 하겠습니다.

 

우리는 먼저 Shape 이라는 인터페이스를 만들고, 그에 대한 구현 객체로 각각 Line과 Oval을 만들어주도록 하겠습니다.

이때 Oval과 Line에 각각 다른 특성을 부여할 건데, Oval은 주어진 색상으로 Oval을 채울지 결정하기 위한 내적 속성(Intrinsic Property)를 가질 것이고, Line은 그러한 내적 속성을 갖고 있지 않을 것입니다.

 

Shape.java

package com.patterns.flyweightpattern;

import java.awt.Color;
import java.awt.Graphics;
 
public interface Shape {
 
    public void draw(Graphics g, int x, int y, int width, int height, Color color);
}

 

Line.java

package com.patterns.flyweightpattern;

import java.awt.Color;
import java.awt.Graphics;
 
public class Line implements Shape {
 
    public Line(){
        System.out.println("Creating Line object");
        //adding time delay
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void draw(Graphics line, int x1, int y1, int x2, int y2, Color color) {
        line.setColor(color);
        line.drawLine(x1, y1, x2, y2);
    }
 
}

Oval.java

package com.patterns.flyweightpattern;

import java.awt.Color;
import java.awt.Graphics;
 
public class Oval implements Shape {
	
    //intrinsic property
    private boolean fill;
	
    public Oval(boolean f){
        this.fill = f;
        System.out.println("Creating Oval object with fill=" + f);
        //adding time delay
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public void draw(Graphics circle, int x, int y, int width, int height, Color color) {
        circle.setColor(color);
        circle.drawOval(x, y, width, height);
        if(fill){
            circle.fillOval(x, y, width, height);
        }
    }
 
}

기서 눈여겨 보실 부분은 Line과 Oval 클래스의 생성자에 Thread.sleep을 적용했다는 것인데요, 이는 인스턴스화 할 때 시간이 많이 걸린다는 것을 조금 더 과장해서 보여주기 위해 넣은 코드입니다. 이를 통해 플라이웨이트 패턴을 쓰면 객체 생성에 있어서 얼마나 시간을 줄일 수 있는지 직관적으로 확인할 수 있습니다.

 

이번에는 위 객체들을 생성하는 데 사용되는 Flyweight Factory를 작성해보겠습니다.

플라이웨이트 팩토리는 클라이언트가 객체의 인스턴스를 생성하는 데 사용됩니다. 그래서 우리는 팩토리 안에 클라이언트는 접근할 수 없는 Map을 두어 객체들을 관리하도록 하겠습니다.

 

클라이언트가 객체에 대한 인스턴스를 얻기 위해 호출할 때, 팩토리 안에 정의해둔 Map에 해당 객체가 있다면 별도의 인스턴스 생성 없이 그대로 Map에서 리턴하고, 만약 없다면 새로 인스턴스를 생성하여 맵에 저장(put) 한 후에 그 객체를 리턴하게 합니다.

 

플라이웨이트 팩토리 클래스는 아래와 같이 작성하겠습니다.

 

ShapeFactory.java

package com.patterns.flyweightpattern;

import java.util.HashMap;

public class ShapeFactory {
 
    private static final HashMap<ShapeType, Shape> shapes = new HashMap<ShapeType, Shape>();
 
    public static Shape getShape(ShapeType type) {
        Shape shapeImpl = shapes.get(type);
 
        if (shapeImpl == null) {
            if (type.equals(ShapeType.OVAL_FILL)) {
                shapeImpl = new Oval(true);
            } else if (type.equals(ShapeType.OVAL_NOFILL)) {
                shapeImpl = new Oval(false);
            } else if (type.equals(ShapeType.LINE)) {
                shapeImpl = new Line();
            }
            shapes.put(type, shapeImpl);
        }
        return shapeImpl;
    }
	
    public static enum ShapeType {
        OVAL_FILL, OVAL_NOFILL, LINE;
    }
}

여기서 눈여겨 보실 것은 Map의 Key 값으로 ShapeType이라는 Enum을 사용한 것인데요, 이렇게 사용하면 타입 안전(Type-safety)하게 사용할 수 있습니다.

 

위 팩토리 클래스를 보시면 getShape() 메소드의 인자 값으로 들어오는 Enum 객체를 shapes라는 HashMap의 키 값으로 해서 이미 저장되어 있는 값인지 검사를 합니다.

만약 맵에 객체가 있다면 별도의 인스턴스 생성 없이 맵에 들어있던 객체를 그대로 리턴하고, 맵에 객체가 없다면 새로운 인스턴스를 만들고 맵에 저장한 후에 생성한 객체를 리턴하게 됩니다.

 

이렇게 플라이웨이트 패턴을 사용하게 된다면 불필요한 인스턴스 생성을 최소화하기 때문에, 필요할 때마다 매번 새로운 인스턴스를 생성할 때보다 훨씬 메모리를 적게 사용하고, 빠른 프로그램을 작성할 수 있게 됩니다.

 

이제 위 코드를 한 번 테스트 해보겠습니다.

 

DrawingClient.java

package com.patterns.flyweightpattern;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

import com.patterns.flyweightpattern.ShapeFactory.ShapeType;
 
public class DrawingClient extends JFrame{
 
    private static final long serialVersionUID = -1350200437285282550L;
    private final int WIDTH;
    private final int HEIGHT;
 
    private static final ShapeType shapes[] = { ShapeType.LINE, ShapeType.OVAL_FILL,ShapeType.OVAL_NOFILL };
    private static final Color colors[] = { Color.RED, Color.GREEN, Color.YELLOW };
	
    public DrawingClient(int width, int height) {
        this.WIDTH = width;
        this.HEIGHT = height;
        Container contentPane = getContentPane();
 
        JButton startButton = new JButton("Draw");
        final JPanel panel = new JPanel();
 
        contentPane.add(panel, BorderLayout.CENTER);
        contentPane.add(startButton, BorderLayout.SOUTH);
        setSize(WIDTH, HEIGHT);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
 
        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                Graphics g = panel.getGraphics();
                for (int i = 0; i < 20; ++i) {
                    Shape shape = ShapeFactory.getShape(getRandomShape());
                    shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(),
                            getRandomHeight(), getRandomColor());
                }
            }
        });
    }
	
    private ShapeType getRandomShape() {
        return shapes[(int) (Math.random() * shapes.length)];
    }
 
    private int getRandomX() {
        return (int) (Math.random() * WIDTH);
    }
 
    private int getRandomY() {
        return (int) (Math.random() * HEIGHT);
    }
 
    private int getRandomWidth() {
        return (int) (Math.random() * (WIDTH / 10));
    }
 
    private int getRandomHeight() {
        return (int) (Math.random() * (HEIGHT / 10));
    }
 
    private Color getRandomColor() {
        return colors[(int) (Math.random() * colors.length)];
    }
 
    public static void main(String[] args) {
        DrawingClient drawing = new DrawingClient(500,600);
    }
}

 

위 클라이언트 코드에서는 Shape의 타입을 랜덤으로 생성하게끔 작성했습니다.

 

만약 실행해보신다면, Line 객체와 Oval 객체가 최초에 생성될 때에만 앞서 생성자에서 설정해두었던 sleep(2000)이 실행되고 이후에는 별도의 딜레이 없이 빠르게 객체가 생성되는 것을 확인할 수 있습니다.

 

<결과>

플라이웨이트 패턴 활용 예

플라이웨이트 패턴은 어디에서 쓰이고 있을까요?

자바의 모든 래퍼 클래스의 valueOf() 메소드가 바로 이 플라이웨이트 패턴을 사용하고 있습니다. 그래서 래퍼 클래스를 생성해야 할 때 new 키워드를 통해 인스턴스를 매번 생성하기보다는 valueOf() 메소드를 통해 생성하는 것이 더 효율적입니다.

 

또, 대표적으로 사용되는 것이 바로 Java의 String Pool 입니다. Java에서는 String Pool을 별도로 두어 같은 문자열에 대해 다시 사용될 때에 새로운 메모리를 할당하는 것이 아니라 String Pool에 있는지 검사해서 있으면 가져오고 없으면 새로 메모리를 할당하여 String Pool에 등록한 후에 사용하도록 하고 있습니다.

 

참조 원문 : https://readystory.tistory.com/137?category=822867

프록시 패턴(Proxy Pattern)

특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)을 제공합니다.

프록시 패턴은 디자인 패턴 중 이해하기 쉬운 패턴에 속합니다.

 

프록시 패턴은 구조 패턴(Structural Pattern) 중 하나로, 어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자(surrogate)나 자리표시자(placeholder)의 역할을 하는 객체를 제공하는 패턴입니다.

 

 

프록시 패턴(Proxy Pattern) 이해 및 예제

프록시 패턴은 어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자(surrogate)나 자리표시자(placeholder)의 역할을 하는 객체를 제공하는 패턴입니다.

 

예를 들어, 우리가 시스템 명령어를 실행하는 객체를 갖고 있을 때 우리가 그 객체를 사용하는 것이라면 괜찮지만 만약 그 객체를 클라이언트에게 제공하려고 한다면 클라이언트 프로그램이 우리가 원치 않는 파일을 삭제하거나 설정을 변경하는 등의 명령을 내릴 수 있기 때문에 심각한 문제를 초래할 수 있습니다.

 

프록시 패턴은 클라이언트에게 접근에 대한 컨트롤을 제공하여 위와 같은 문제를 해결합니다.

 

예제를 살펴보겠습니다.

 

먼저 CommandExecutor 라는 명령을 실행하는 메소드를 제공하는 인터페이스를 정의하겠습니다.

 

CommandExecutor.java

package com.patterns.proxypattern;

public interface CommandExecutor {
	 
    public void runCommand(String cmd) throws Exception;
}

 

자, 이번에는 만약 프록시 패턴을 적용하지 않고 앞서 말씀드린 것처럼 그냥 클라이언트에게 명령에 대한 권한을 전부 넘겨주는 객체를 생성해보겠습니다.

 

CommandExecutorImpl.java

package com.patterns.proxypattern;

import java.io.IOException;

public class CommandExecutorImpl implements CommandExecutor {
	 
    @Override
    public void runCommand(String cmd) throws IOException {
        //some heavy implementation
        Runtime.getRuntime().exec(cmd);
        System.out.println("'" + cmd + "' command executed.");
    }
}

 

보시다시피 CommandExecutorImpl 클래스에서는 runCommand()의 파라미터로 받은 cmd 명령어를 그대로 수행하고 있습니다. 이렇게 구현할 경우에는 앞서 말씀드린 바와 같이 원치 않는 파일 삭제나 설정 변경 등에 문제가 발생할 가능성이 높아집니다.

 

그렇다면 이를 해결하기 위해 프록시 객체를 두어 관리자(Admin) 계정이 아닐 경우에는 rm 이라는 명령어에 대해 수행하지 못하도록 구현해보도록 하겠습니다.

 

CommandExecutorProxy.java

package com.patterns.proxypattern;

public class CommandExecutorProxy implements CommandExecutor {
    private boolean isAdmin;
    private CommandExecutor executor;
	
    public CommandExecutorProxy(String user, String pwd){
        if("ReadyKim".equals(user) && "correct_pwd".equals(pwd))
            isAdmin = true;
        executor = new CommandExecutorImpl();
    }
	
    @Override
    public void runCommand(String cmd) throws Exception {
        if(isAdmin){
            executor.runCommand(cmd);
        }else{
            if(cmd.trim().startsWith("rm")){
                throw new Exception("rm command is not allowed for non-admin users.");
            }else{
                executor.runCommand(cmd);
            }
        }
    }
}

 

 

자 이번에는 위에서 작성한 코드들을 테스트 해보도록 하겠습니다.

 

ProxyPatternTest.java

package com.patterns.proxypattern;

public class ProxyPatternTest {
	 
    public static void main(String[] args){
        CommandExecutor executor = new CommandExecutorProxy("ReadyKim", "wrong_pwd");
        try {
            executor.runCommand("ls -ltr");
            executor.runCommand("rm -rf abc.pdf");
        } catch (Exception e) {
            System.out.println("Exception Message::"+e.getMessage());
        }	
    }
}

 

결과에서 보다시피 Admin 계정의 ID와 Password가 틀렸기 때문에 프록시 객체가 rm 명령어에 대한 수행을 거부하였고 그 결과로 Exception을 던지게 됐습니다.

 

프록시 패턴은 이렇듯 어떤 객체에 대하여 접근할 때에 Wrapper Class를 두어 접근에 대한 통제(Control access)를 위해 사용합니다.

  Composite 패턴은 구조 패턴 중 하나로, 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴입니다. 사용자는 이 복합체 패턴을 통해 단일 객체와 복합 객체 모두 동일하게 다룰 수 있습니다.

 

컴포지트 패턴으로 객체를 트리구조로 구성해서 부분-전체 계층구조를 구현합니다. 컴포지트 패턴을 사용하면 클라이언트에서 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있습니다.

 

구조 패턴(Structural Pattern)이란?

구조 패턴이란 작은 클래스들을 상속과 합성을 이용하여 더 큰 클래스를 생성하는 방법을 제공하는 패턴입니다.

이 패턴을 사용하면 서로 독립적으로 개발한 클래스 라이브러리를 마치 하나인 양 사용할 수 있습니다. 또, 여러 인터페이스를 합성(Composite)하여 서로 다른 인터페이스들의 통일된 추상을 제공합니다.

 

구조 패턴의 중요한 포인트는 인터페이스나 구현을 복합하는 것이 아니라 객체를 합성하는 방법을 제공한다는 것입니다. 이는 컴파일 단계에서가 아닌 런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖습니다.

 

복합체 패턴(Composite Pattern) 개념 및 예제 코드

복합체 패턴에 대해 실생활에서 예를 들어 설명해보겠습니다.

 

파워포인트에 삼각형, 사각형, 원을 각각 하나씩 만들어 놓고 삼각형과 사각형을 그룹화했다고 가정하겠습니다. 이제 우리는 모든 도형을 빨간색으로 색을 칠하려고 합니다.

이때 우리가 채우기 버튼을 누를 때 선택하는 것이 어떤 도형인지, 혹은 그룹인지에 대해서 구분하지 않아도 됩니다. 파워포인트에서는 도형 하나에 대한 채우기와 그룹 전체에 대한 채우기 버튼이 같습니다.

이처럼 복합체 패턴은 전체 도형들을 하나의 도형을 다루듯이 관리할 수 있다는 특징을 지닙니다.

쉽게 말해 "일괄적인 관리"가 가능한 것이지요.

 

복합체 패턴은 다음과 같은 오브젝트들을 갖습니다.

  • Base Component - 베이스 컴포넌트는 클라이언트가 composition 내의 오브젝트들을 다루기 위해 제공되는 인터페이스를 말합니다. 베이스 컴포넌트는 인터페이스 또는 추상 클래스로 정의되며 모든 오브젝트들에게 공통되는 메소드를 정의해야 합니다.
  • Leaf - composition 내 오브젝트들의 행동을 정의합니다. 이는 복합체를 구성하는 데 중요한 요소이며, 베이스 컴포넌트를 구현합니다. 그리고 Leaf는 다른 컴포넌트에 대해 참조를 가지면 안됩니다. 
  • Composite - Leaf 객체들로 이루어져 있으며 베이스 컴포넌트 내 명령들을 구현합니다.

 

예제를 통해 이해를 돕도록 하겠습니다.

 

1. Base Component

Base Component는 Leaf와 Composite의 공통되는 메소드들을 정의해야 합니다.

예제에서는 Shape 인터페이스 내에 각각 도형마다 색을 입히는 draw() 메소드를 정의하도록 하겠습니다.

 

Shape.java

package com.patterns.composite;

public interface Shape {
	public void draw(String fillColor);
}

 

2. Leaf Objects

Leaf 객체들은 복합체에 포함되는 요소로, Base Component를 구현해야 합니다.

예제에서는 Triangle과 Circle 클래스를 정의하도록 하겠습니다.

 

Triangle.java

package com.patterns.composite;

public class Triangle implements Shape {
	 
    @Override
    public void draw(String fillColor) {
    	
        System.out.println("Drawing Triangle with color "+fillColor);
    }
}

Circle.java

package com.patterns.composite;

public class Circle implements Shape {
	 
    @Override
    public void draw(String fillColor) {
        System.out.println("Drawing Circle with color "+fillColor);
    }
}

3. Composite Objects

Composite 객체는 Leaf 객체들을 포함하고 있으며, Base Component를 구현할 뿐만 아니라 Leaf 그룹에 대해 add와 remove를 할 수 있는 메소드들을 클라이언트에게 제공합니다.

 

Drawing.java

package com.patterns.composite;

import java.util.ArrayList;
import java.util.List;

public class Drawing implements Shape {
	 
    //collection of Shapes
    private List<Shape> shapes = new ArrayList<Shape>();
	
    @Override
    public void draw(String fillColor) {
        for(Shape sh : shapes) {
            sh.draw(fillColor);
        }
    }
	
    //adding shape to drawing
    public void add(Shape s) {
        this.shapes.add(s);
    }
	
    //removing shape from drawing
    public void remove(Shape s) {
        shapes.remove(s);
    }
	
    //removing all the shapes
    public void clear() {
        System.out.println("Clearing all the shapes from drawing");
        this.shapes.clear();
    }
}

 

여기서 중요한 것은 Composite Object 또한 Base Component를 구현해야 한다는 것입니다.

그렇게 해야만 클라이언트가 Composite 객체에 대해서 다른 Leaf들과 동일하게 취급될 수 있습니다.

 

아직까지 이해가 잘 안되더라도 괜찮습니다.

이제 우리가 만든 예제 코드를를 테스트하는 코드를 보면 확실하게 이해 가실 것입니다.

 

TestCompositePattern.java

package com.patterns.composite;

import java.util.ArrayList;
import java.util.List;

public class TestCompositePattern {
	 
    public static void main(String[] args) {
    	
        Shape tri = new Triangle();
        Shape tri1 = new Triangle();
        Shape cir = new Circle();
		
        Drawing drawing = new Drawing();
        drawing.add(tri1);
        drawing.add(tri1);
        drawing.add(cir);
		
        drawing.draw("Red");
		
        List<Shape> shapes = new ArrayList<>();
        shapes.add(drawing);
        shapes.add(new Triangle());
        shapes.add(new Circle());
        
        for(Shape shape : shapes) {
            shape.draw("Green");
        }
    }
}

 

자, 위 코드를 보시면 drawing 객체를 통해 Triangle, Circle 등의 Leaf 객체들을 Group으로 묶어서 한 번에 동작을 수행할 수 있습니다. 나아가 drawing 객체 또한 다른 도형들과 마찬가지로 Shape 인터페이스를 구현하고 있기 때문에 변수 shapes에서 살펴보는 것과 같이 drawing이 다른 도형들과 함께 취급될 수 있습니다.

Drawing Triangle with color Red
Drawing Triangle with color Red
Drawing Circle with color Red
Drawing Triangle with color Green
Drawing Triangle with color Green
Drawing Circle with color Green
Drawing Triangle with color Green
Drawing Circle with color Green

어댑터 패턴은 구조 패턴(Structural Pattern) 중 하나로, 서로 관계없는 인터페이스들을 함께 사용할 수 있게 하는 패턴입니다.

 즉 특정 틀래스 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환합니다. 인터페이스가 호환되지 않아 같이 쓸 수 없었던 클래스를 사용할 수 있게 도와줍니다.

 

어댑터 패턴(Adapter Pattern) 개념 및 예제 코드

어댑터가 일상생활에서 쓰이는 용도를 생각해보시면 인풋과 아웃풋이 다른, 이를테면 해외여행 갈 때 챙기는 볼트 변환기(돼지코) 같은 것들을 어댑터라고 표현하기도 합니다.

마찬가지로 어댑터 패턴은 클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 변환시키는 패턴입니다.

어댑터 패턴은 서로 일치하지 않는 인터페이스를 갖는 클래스들을 함께 동작시킵니다.

 

위에서 말씀드린 것처럼 볼트 변환기 개념을 가지고서 예제코드를 작성해보도록 하겠습니다.

 

Volt 클래스는 단순히 volt 값을 가지고 있는 POJO 클래스입니다.

package com.patterns.adapterPattern;

public class Volt {
	
	private int volts;

	public Volt(int v) {
		this.volts = v;
	}

	public int getVolts() {
		return volts;
	}

	public void setVolts(int volts) {
		this.volts = volts;
	}
}

Socket 클래스는 단순히 120 볼트를 값으로 갖는 볼트 객체를 생성하는 클래스입니다. 

package com.patterns.adapterPattern;

public class Socket {
	
	public Volt getVolt() {
		return new Volt(120);
	}
	
}

 

이제 우리는 120볼트뿐만 아니라 3볼트와 12볼트도 추가로 생성하는 어댑터를 만들어볼 것인데, 이를 위해 각각의 볼트 객체 생성을 위한 인터페이스를 정의해주겠습니다.

package com.patterns.adapterPattern;

public interface SocketAdapter {
	
	public Volt get120Volt();
	public Volt get12Volt();
	public Volt get3Volt();
}

 

어댑터 패턴을 구현하기 위해서는 Class Adapter Object Adapter 두 가지 방법이 있습니다.

어떤 방법으로 구현하던 결과는 같습니다.

 

  • Class Adapter  - 자바의 상속(Inheritance)을 이용한 방법입니다.
  • Object Adapter - 자바의 합성(Composite)을 이용한 방법입니다.

이제 우리는 Socket 클래스와 Volt 클래스를 SocketAdapter 인터페이스에서 정의한 메소드에 맞춰서 사용할 것입니다.

그러면 Class Adpater와 Object Adapter 방식으로 각각 예제코드를 작성해보겠습니다.

 

Class Adapter 방식

package com.patterns.adapterPattern;

//Using inheritance for adapter pattern
public class SocketClassAdapterImpl extends Socket implements SocketAdapter {

	@Override
	public Volt get120Volt() {
		return getVolt();
	}

	@Override
	public Volt get12Volt() {
		Volt v = getVolt();
		return convertVolt(v, 10);
	}

	@Override
	public Volt get3Volt() {
		Volt v = getVolt();
		return convertVolt(v, 40);
	}

	private Volt convertVolt(Volt v, int i) {
		return new Volt(v.getVolts() / i);
	}

}

 

Object Adapter 방식

package com.patterns.adapterPattern;

public class SocketObjectAdapterImpl implements SocketAdapter {

	// Using Composition for adapter pattern
	private Socket sock = new Socket();

	@Override
	public Volt get120Volt() {
		return sock.getVolt();
	}

	@Override
	public Volt get12Volt() {
		Volt v = sock.getVolt();
		return convertVolt(v, 10);
	}

	@Override
	public Volt get3Volt() {
		Volt v = sock.getVolt();
		return convertVolt(v, 40);
	}

	private Volt convertVolt(Volt v, int i) {
		return new Volt(v.getVolts() / i);
	}
}

눈여겨 볼 것은 두 가지 방식 모두 SocketAdapter 인터페이스를 구현했다는 것입니다. 이 어댑터 인터페이스는 추상 클래스로 사용해도 무방합니다.

 

이제 이 어댑터 인터페이스를 사용 하여 테스트 코드를 작성해보겠습니다.

이렇게 해서 어댑터 패턴에 대해 살펴봤습니다.

 

이 어댑터 패턴은 Java의 JDK 안에서 Arrays.asList()나 InputStreamReader(InputStream), OutputStreamWriter(OutputStream) 등에서 사용되었습니다.

 

참고 사이트 : https://readystory.tistory.com/125?category=822867 

 

[구조 패턴] 어댑터 패턴(Adapter Pattern) 이해 및 예제

어댑터 패턴은 구조 패턴(Structural Pattern) 중 하나로, 서로 관계없는 인터페이스들을 함께 사용할 수 있게 하는 패턴입니다. 구조 패턴(Structural Pattern)이란? 구조 패턴이란 작은 클래스들을 상속과

readystory.tistory.com

                  https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

www.hanbit.co.kr

 

프로토타입은 주로 실제 제품을 만들기에 앞서 대략적인 샘플 정도의 의미로 사용되는 단어입니다.

프로토타입 패턴은 객체를 생성하는 데 비용(시간과 자원)이 많이 들고, 비슷한 객체가 이미 있는 경우에 사용되는 생성 패턴 중 하나입니다.

프로토타입 패턴은 Original 객체를 새로운 객체에 복사하여 우리의 필요에 따라 수정하는 메커니즘을 제공합니다.

이 패턴은 복사를 위하여 Java에서 제공하는 clone()을 사용합니다.

 

프로토타입 패턴은 생성 패턴(Creational Pattern) 중 하나이다.

생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다.

생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현 방법을 시스템과 분리해줍니다.

생성 패턴은 시스템이 상속(inheritance) 보다 복합(composite) 방법을 사용하는 방향으로 진화되어 가면서 더 중요해지고 있습니다.

 

생성 패턴에서는 중요한 이슈가 두 가지 있습니다.

  1. 생성 패턴은 시스템이 어떤 Concrete Class를 사용하는지에 대한 정보를 캡슐화합니다.
  2. 생성 패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 결합하는지에 대한 부분을 완전히 가려줍니다.

쉬운 말로 정리하자면, 생성 패턴을 이용하면 무엇이 생성되고, 누가 이것을 생성하며, 이것이 어떻게 생성되는지, 언제 생성할 것인지 결정하는 데 유연성을 확보할 수 있게 됩니다.

 

 

프로토타입 패턴(Prototype Pattern)의 이해 및 예제

앞서 말씀드린 것처럼 프로토타입 패턴은 객체를 생성하는 데 시간과 노력이 많이 들고, 이미 유사한 객체가 존재하는 경우에 사용됩니다. 그리고 java의 clone()을 이용하기 때문에 생성하고자 하는 객체에 clone에 대한 Override를 요구합니다. 이때 주의할 점은 반드시 생성하고자 하는 객체의 클래스에서 clone()이 정의되어야 한다는 것입니다.

 

예를 들어 DB로부터 데이터를 가져오는 객체가 존재한다고 가정해보겠습니다.

만약 DB로부터 가져온 데이터를 우리의 프로그램에서 수차례 수정을 해야하는 요구사항이 있는 경우, 매번 new 라는 키워드를 통해 객체를 생성하여 DB로부터 항상 모든 데이터를 가져오는 것은 좋은 아이디어가 아닙니다.

왜냐하면 DB로 접근해서 데이터를 가져오는 행위는 비용이 크기 때문입니다.

따라서 한 번 DB에 접근하여 데이터를 가져온 객체를 필요에 따라 새로운 객체에 복사하여 데이터 수정 작업을 하는 것이 더 좋은 방법입니다.

이때 객체의 복사를 얕은 복사(shallow copy)로 할 지, 깊은 복사(deep copy)로 할 지에 대해서는 선택적으로 행하시면 됩니다.

 

샘플 코드를 통해 이해를 돕도록 하겠습니다.

실제 DB와 연동되는 샘플 코드를 작성하는 것은 다소 복잡할 수 있으니 쉽게 직원의 명단을 갖고 있는 Employees 클래스를 통해 살펴보겠습니다.

 

package com.patterns.Prototype;

import java.util.ArrayList;
import java.util.List;

public class Employees implements Cloneable {
	
	private List<String> empList;

	public Employees() {
		empList = new ArrayList<String>();
	}

	public Employees(List<String> list) {
		this.empList = list;
	}

	public void loadData() {
		// read all employees from database and put into the list
		empList.add("Pankaj");
		empList.add("Raj");
		empList.add("David");
		empList.add("Lisa");
	}

	public List<String> getEmpList() {
		return empList;
	}

	@Override
	public Object clone() throws CloneNotSupportedException {
		
		List<String> temp = new ArrayList<String>();
		for (String s : this.empList) {
			temp.add(s);
		}
		return new Employees(temp);
	}
}

위 코드를 보시면 clone() 메소드를 재정의하기 위해 Cloneable 인터페이스를 구현한 것을 확인할 수 있습니다. 여기서 사용되는 clone()은 empList에 대하여 깊은 복사(deep copy)를 실시합니다.

 

이번에는 위에서 작성한 코드를 메인에서 테스트해보도록 하겠습니다.

package com.patterns.Prototype;

import java.util.List;

public class PrototypePatternTest {
	public static void main(String[] args) throws CloneNotSupportedException {
		Employees emps = new Employees();
		emps.loadData();

		// Use the clone method to get the Employee object
		Employees empsNew = (Employees) emps.clone();
		Employees empsNew1 = (Employees) emps.clone();
		List<String> list = empsNew.getEmpList();
		list.add("John");
		List<String> list1 = empsNew1.getEmpList();
		list1.remove("Pankaj");

		System.out.println("emps List: " + emps.getEmpList());
		System.out.println("empsNew List: " + list);
		System.out.println("empsNew1 List: " + list1);
	}
}

결과

emps List: [Pankaj, Raj, David, Lisa]
empsNew List: [Pankaj, Raj, David, Lisa, John]
empsNew1 List: [Raj, David, Lisa]

만약 Employees 클래스에서 clone()을 제공하지 않았다면, DB로부터 매번 employee 리스트를 직접 가져와야 했을 것이고, 그로 인해 상당히 큰 비용이 발생했을 것입니다.

하지만 프로토타입을 사용한다면 1회의 DB 접근을 통해 가져온 데이터를 복사하여 사용한다면 이를 해결할 수 있습니다. (객체를 복사하는 것이 네트워크 접근이나 DB 접근보다 훨씬 비용이 적습니다.)

 

참고자료(원문) : https://readystory.tistory.com/122?category=822867 

 

[생성 패턴] 프로토타입 패턴(Prototype Pattern) 이해 및 예제

프로토타입은 주로 실제 제품을 만들기에 앞서 대략적인 샘플 정도의 의미로 사용되는 단어입니다. 프로토타입 패턴은 객체를 생성하는 데 비용(시간과 자원)이 많이 들고, 비슷한 객체가 이미

readystory.tistory.com

 

빌더 패턴은 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴입니다. 빌더 패턴은 생성해야 되는 객체가 Optional한 속성을 많이 가질 때 빛을 발휘합니다.

 

빌더 패턴은 생성 패턴(Creational Pattern) 중 하나이다.

생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다.

생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현 방법을 시스템과 분리해줍니다.

생성 패턴은 시스템이 상속(inheritance) 보다 복합(composite) 방법을 사용하는 방향으로 진화되어 가면서 더 중요해지고 있습니다.

 

생성 패턴에서는 중요한 이슈가 두 가지 있습니다.

  1. 생성 패턴은 시스템이 어떤 Concrete Class를 사용하는지에 대한 정보를 캡슐화합니다.
  2. 생성 패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 결합하는지에 대한 부분을 완전히 가려줍니다.

쉬운 말로 정리하자면, 생성 패턴을 이용하면 무엇이 생성되고, 누가 이것을 생성하며, 이것이 어떻게 생성되는지, 언제 생성할 것인지 결정하는 데 유연성을 확보할 수 있게 됩니다.

 

빌더 패턴(Builder Pattern)의 개념 및 예제

빌더 패턴은 많은 Optional한 멤버 변수(혹은 파라미터)나 지속성 없는 상태 값들에 대해 처리해야 하는 문제들을 해결합니다.

 

예를 들어, 팩토리 패턴이나 추상 팩토리 패턴에서는 생성해야하는 클래스에 대한 속성 값이 많을 때 아래와 같은 이슈들이 있습니다.

  1.  클라이언트 프로그램으로부터 팩토리 클래스로 많은 파라미터를 넘겨줄 때 타입, 순서 등에 대한 관리가 어려워져 에러가 발생할 확률이 높아집니다.
  2. 경우에 따라 필요 없는 파라미터들에 대해서 팩토리 클래스에 일일이 null 값을 넘겨줘야 합니다.
  3. 생성해야 하는 sub class가 무거워지고 복잡해짐에 따라 팩토리 클래스 또한 복잡해집니다. 

 

빌더 패턴은 이러한 문제들을 해결하기 위해 별도의 Builder 클래스를 만들어 필수 값에 대해서는 생성자를 통해, 선택적인 값들에 대해서는 메소드를 통해 step-by-step으로 값을 입력받은 후에 build() 메소드를 통해 최종적으로 하나의 인스턴스를 리턴하는 방식입니다.

 

빌더 패턴은 굉장히 자주 사용되는 생성 패턴 중 하나로, Retrofit이나 Okhttp 등 유명 오픈소스에서도 이 빌더 패턴을 사용하고 있습니다.

 

빌더 패턴을 구현하는 방법은 아래와 같습니다.

  1. 빌더 클래스를 Static Nested Class로 생성합니다. 이때, 관례적으로 생성하고자 하는 클래스 이름 뒤에 Builder를 붙입니다. 예를 들어, Computer 클래스에 대한 빌더 클래스의 이름은 ComputerBuilder 라고 정의합니다.  (Nested :중첩)
  2. 빌더 클래스의 생성자는 public으로 하며, 필수 값들에 대해 생성자의 파라미터로 받습니다.
  3. 옵셔널한 값들에 대해서는 각각의 속성마다 메소드로 제공하며, 이때 중요한 것은 메소드의 리턴 값이 빌더 객체 자신이어야 합니다.
  4. 마지막 단계로, 빌더 클래스 내에 build() 메소드를 정의하여 클라이언트 프로그램에게 최종 생성된 결과물을 제공합니다. 이렇듯 build()를 통해서만 객체 생성을 제공하기 때문에 생성 대상이 되는 클래스의 생성자는 private으로 정의해야 합니다.

 

구현 방법만 봐서는 잘 이해가 가지 않을 수 있으니, 간단한 예제를 통해 이해를 돕도록 하겠습니다.

예제는 ComputerBuilder 클래스를 통해 Computer 클래스 객체를 생성하는 샘플 코드입니다. 

package com.patterns.builder;

public class Computer {
	
	// required parameters
	private String HDD;
	private String RAM;

	// optional parameters
	private boolean isGraphicsCardEnabled;
	private boolean isBluetoothEnabled;

	public String getHDD() {
		return HDD;
	}

	public String getRAM() {
		return RAM;
	}

	public boolean isGraphicsCardEnabled() {
		return isGraphicsCardEnabled;
	}

	public boolean isBluetoothEnabled() {
		return isBluetoothEnabled;
	}

	private Computer(ComputerBuilder builder) {
		this.HDD = builder.HDD;
		this.RAM = builder.RAM;
		this.isGraphicsCardEnabled = builder.isGraphicsCardEnabled;
		this.isBluetoothEnabled = builder.isBluetoothEnabled;
	}

	// Builder Class
	public static class ComputerBuilder {

		// required parameters
		private String HDD;
		private String RAM;

		// optional parameters
		private boolean isGraphicsCardEnabled;
		private boolean isBluetoothEnabled;

		public ComputerBuilder(String hdd, String ram) {
			this.HDD = hdd;
			this.RAM = ram;
		}

		public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
			this.isGraphicsCardEnabled = isGraphicsCardEnabled;
			return this;
		}

		public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
			this.isBluetoothEnabled = isBluetoothEnabled;
			return this;
		}

		public Computer build() {
			return new Computer(this);
		}

	}
}

여기서 살펴볼 것은 Computer 클래스가 setter 메소드 없이 getter 메소드만 가진다는 것과 public 생성자가 없다는 것입니다. 그렇기 때문에 Computer 객체를 얻기 위해서는 오직 ComputerBuilder 클래스를 통해서만 가능합니다.

 

이제 이렇게 작성한 예제를 클라이언트에서 사용해보도록 하겠습니다.

package com.patterns.builder;

public class TestBuilderPattern {
	 
    public static void main(String[] args) {
        Computer comp = new Computer.ComputerBuilder("500 GB", "2 GB")
                .setBluetoothEnabled(true)
                .setGraphicsCardEnabled(true)
                .build();
    }
 
}

보시는 것처럼 Computer 객체를 얻기 위해 ComputerBuilder 클래스를 사용하고 있으며 필수 값인 HDD와 RAM 속성에 대해서는 생성자로 받고 Optional한 값인 BluetoothEnabled와 GraphicsCardEnabled에 대해서는 메소드를 통해 선택적으로 입력 받고 있습니다.

즉, BluetoothEnabled 값이 필요 없는 객체라면 setBluetoothEnabled() 메소드를 사용하지 않으면 됩니다.

 

참고자료(원문) :  https://readystory.tistory.com/121?category=822867 

 

팩토리 패턴은 생성 패턴(Creational Pattern) 중 하나이다.

또한 팩토리 메소드 패턴(Factory Method Pattern)이다 

 

생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다.

생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현 방법을 시스템과 분리해줍니다.

생성 패턴은 시스템이 상속(inheritance) 보다 복합(composite) 방법을 사용하는 방향으로 진화되어 가면서 더 중요해지고 있습니다.

 

생성 패턴에서는 중요한 이슈가 두 가지 있습니다.

  1. 생성 패턴은 시스템이 어떤 Concrete Class를 사용하는지에 대한 정보를 캡슐화합니다.
  2. 생성 패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 결합하는지에 대한 부분을 완전히 가려줍니다.

쉬운 말로 정리하자면, 생성 패턴을 이용하면 무엇이 생성되고, 누가 이것을 생성하며, 이것이 어떻게 생성되는지, 언제 생성할 것인지 결정하는 데 유연성을 확보할 수 있게 됩니다.

 

팩토리 패턴이란?

팩토리 패턴은 객체를 생성하는 인터페이스는 미리 정의하되, 인스턴스를 만들 클래스의 결정은 서브 클래스 쪽에서 내리는 패턴입니다. 다시 말해 여러 개의 서브 클래스를 가진 슈퍼 클래스가 있을 때 인풋에 따라 하나의 자식 클래스의 인스턴스를 리턴해주는 방식입니다.

팩토리 패턴에서는 클래스의 인스턴스를 만드는 시점을 서브 클래스로 미룹니다.

이 패턴은 인스턴스화에 대한 책임을 객체를 사용하는 클라이언트에서 팩토리 클래스로 가져옵니다. 

 

활용성

  • 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
  • 생성할 객체를 기술하는 책임을 자신의 서브클래스가 지정했으면 할 때

1) super class 작성 

package com.patterns.abstractFactory1;

public abstract class Computer {

	public abstract String getRAM();
	public abstract String getHDD();
	public abstract String getCPU();

	@Override
	public String toString() {
		return "RAM= " + this.getRAM() + ", HDD=" + this.getHDD() + ", CPU=" + this.getCPU();
	}
}

2) sub class작성  : PC,Server ...

package com.patterns.abstractFactory1;

public class PC extends Computer {
 
    private String ram;
    private String hdd;
    private String cpu;
     
    public PC(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    
    @Override
    public String getRAM() {
        return this.ram;
    }
 
    @Override
    public String getHDD() {
        return this.hdd;
    }
 
    @Override
    public String getCPU() {
        return this.cpu;
    }
 
}
package com.patterns.abstractFactory1;

public class Server extends Computer {

	private String ram;
	private String hdd;
	private String cpu;

	public Server(String ram, String hdd, String cpu) {
		this.ram = ram;
		this.hdd = hdd;
		this.cpu = cpu;
	}

	@Override
	public String getRAM() {
		return this.ram;
	}

	@Override
	public String getHDD() {
		return this.hdd;
	}

	@Override
	public String getCPU() {
		return this.cpu;
	}

3) 테스트

package com.patterns.Factory;

public class ComputerFactory {

	public static Computer getComputer(String type, String ram, String hdd, String cpu) {
		if ("PC".equalsIgnoreCase(type))
			return new PC(ram, hdd, cpu);
		else if ("Server".equalsIgnoreCase(type))
			return new Server(ram, hdd, cpu);

		return null;
	}
}

class TestFactory {
	 
    public static void main(String[] args) {
        Computer pc = ComputerFactory.getComputer("pc","2 GB","500 GB","2.4 GHz");
        Computer server = ComputerFactory.getComputer("server","16 GB","1 TB","2.9 GHz");
        System.out.println("Factory PC Config::"+pc);
        System.out.println("Factory Server Config::"+server);
    }
 
}

 

팩토리 패턴의 장점

  1. 팩토리 패턴은 클라이언트 코드로부터 서브 클래스의 인스턴스화를 제거하여 서로 간의 종속성을 낮추고, 결합도를 느슨하게 하며(Loosely Coupled), 확장을 쉽게 합니다.
    예를 들어, 위 예제에서 작성한 클래스 중 PC class에 대해 수정 혹은 삭제가 일어나더라도 클라이언트는 알 수 없기 때문에 코드를 변경할 필요도 없습니다.
  2. 팩토리 패턴은 클라이언트와 구현 객체들 사이에 추상화를 제공합니다.

 

사용 예

  1. java.util 패키지에 있는 Calendar, ResourceBundle, NumberFormat 등의 클래스에서 정의된 getInstance() 메소드가 팩토리 패턴을 사용하고 있습니다.
  2. Boolean, Integer, Long 등 Wrapper class 안에 정의된 valueOf() 메소드 또한 팩토리 패턴을 사용했습니다.

추가 정의

객체를 생성할 때 필요한 인터페이스를 만듭니다. 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정합니다. 팩토리 메소드 패턴을 사용하면 클래스 인스턴스 만드는 일을 서브클래스에게 맡기게 됩니다.

 

참고문언 : https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

www.hanbit.co.kr

                https://readystory.tistory.com/117

 

[생성 패턴] 팩토리 패턴(Factory Pattern) 이해 및 예제

이번에 살펴볼 디자인 패턴은 가장 유명한 디자인 패턴 중 하나인 팩토리 패턴(Factory Pattern)입니다. 이 팩토리 패턴은 조금 더 구체적인 용어인 팩토리 메소드 패턴(Factory Method Pattern)으로도 널리

readystory.tistory.com

 

추상 팩토리 패턴은 생성 패턴(Creational Pattern) 중 하나이다.

생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다.

생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체의 표현 방법을 시스템과 분리해줍니다.

생성 패턴은 시스템이 상속(inheritance) 보다 복합(composite) 방법을 사용하는 방향으로 진화되어 가면서 더 중요해지고 있습니다.

 

생성 패턴에서는 중요한 이슈가 두 가지 있습니다.

  1. 생성 패턴은 시스템이 어떤 Concrete Class를 사용하는지에 대한 정보를 캡슐화합니다.
  2. 생성 패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 결합하는지에 대한 부분을 완전히 가려줍니다.

쉬운 말로 정리하자면, 생성 패턴을 이용하면 무엇이 생성되고, 누가 이것을 생성하며, 이것이 어떻게 생성되는지, 언제 생성할 것인지 결정하는 데 유연성을 확보할 수 있게 됩니다.

 

추상 팩토리 패턴(Abstract Factory Pattern)이란?

팩토리 메소드 패턴에서는 하나의 팩토리 클래스가 인풋으로 들어오는 값에 따라 if-else나 switch 문을 사용하여 다양한 서브클래스를 리턴하는 형식으로 구현했었습니다.

 

추상 팩토리 패턴에서는 팩토리 클래스에서 서브 클래스를 생성하는 데에 있어 이러한 if-else 문을 걷어냅니다.

추상 팩토리 패턴은 인풋으로 서브클래스에 대한 식별 데이터를 받는 것이 아니라 또 하나의 팩토리 클래스를 받습니다.

 

 1) super class 작성 

package com.patterns.abstractFactory1;

public abstract class Computer {

	public abstract String getRAM();
	public abstract String getHDD();
	public abstract String getCPU();

	@Override
	public String toString() {
		return "RAM= " + this.getRAM() + ", HDD=" + this.getHDD() + ", CPU=" + this.getCPU();
	}
}

2) sub class작성  : PC,Server ...

package com.patterns.abstractFactory1;

public class PC extends Computer {
 
    private String ram;
    private String hdd;
    private String cpu;
     
    public PC(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    
    @Override
    public String getRAM() {
        return this.ram;
    }
 
    @Override
    public String getHDD() {
        return this.hdd;
    }
 
    @Override
    public String getCPU() {
        return this.cpu;
    }
 
}
package com.patterns.abstractFactory1;

public class Server extends Computer {

	private String ram;
	private String hdd;
	private String cpu;

	public Server(String ram, String hdd, String cpu) {
		this.ram = ram;
		this.hdd = hdd;
		this.cpu = cpu;
	}

	@Override
	public String getRAM() {
		return this.ram;
	}

	@Override
	public String getHDD() {
		return this.hdd;
	}

	@Override
	public String getCPU() {
		return this.cpu;
	}

}

여기까지는 팩토리 패턴과 동일

 

3)추상 팩토리의 역활을 하는 인터페이스 또는 추상클래스가 필요함 -> Factroy

package com.patterns.abstractFactory1;

public interface ComputerAbstractFactory {

	public Computer createComputer();

}

 

  여기서 주의 깊게 보실 것은 위에서 작성한 팩토리 인터페이스의 createComputer() 메소드의 리턴 타입이 super class인 Computer라는 것입니다. 이제 이 팩토리 인터페이스를 구현(implements)하는 클래스에서 createComputer() 메소드를 오버라이딩 하여 각각의 서브 클래스를 리턴해줄 것입니다. 이는 자바의 다형성을 아주 잘 활용한 방식이라 볼 수 있습니다.

4) PCFactory, ServerFactory ...

package com.patterns.abstractFactory1;

public class PCFactory implements ComputerAbstractFactory {

	private String ram;
	private String hdd;
	private String cpu;

	public PCFactory(String ram, String hdd, String cpu) {
		this.ram = ram;
		this.hdd = hdd;
		this.cpu = cpu;
	}

	@Override
	public Computer createComputer() {
		return new PC(ram, hdd, cpu);
	}
}

 

package com.patterns.abstractFactory1;

public class ServerFactory implements ComputerAbstractFactory {

	private String ram;
	private String hdd;
	private String cpu;

	public ServerFactory(String ram, String hdd, String cpu) {
		this.ram = ram;
		this.hdd = hdd;
		this.cpu = cpu;
	}

	@Override
	public Computer createComputer() {
		return new Server(ram, hdd, cpu);
	}

}

 

5) 제 마지막으로 이 서브 클래스들을 생성하기 위해 클라이언트 코드에 접점으로 제공되는 컨슈머 클래스(consumer class)를 만들어보겠습니다.

 ※ consumer -> 소비자

package com.patterns.abstractFactory1;

public class ComputerFactory {

	public static Computer getComputer(ComputerAbstractFactory factory) {
		return factory.createComputer();
	}
}

제 클라이언트는 이 ComputerFacotry 클래스의 getComputer()라는 static 메소드에 앞서 구현한 PCFactory나 ServerFactory 인스턴스를 넣어줌으로써if-else 없이도각각 원하는 서브 클래스의 인스턴스를 생성할 수 있게 됐습니다.

 

6) test

package com.patterns.abstractFactory1;

public class AbstractFactoryTest {
	
	public static void main(String[] args) {
		Computer pc = ComputerFactory.getComputer(new PCFactory("2 GB", "500 GB", "2.4 GHz"));
		Computer server = ComputerFactory.getComputer(new ServerFactory("16 GB", "1 TB", "2.9 GHz"));
		System.out.println("AbstractFactory PC Config::" + pc);
		System.out.println("AbstractFactory Server Config::" + server);
	}
}

 

결국 모델은 

추상클래스의 다이어그램

 

추상 팩토리 패턴의 장점

  • 추상 팩토리 패턴은 구현(Implements)보다 인터페이스(Interface)를 위한 코드 접근법을 제공합니다.
    위 예에서 getComputer() 메소드는 파라미터로 인터페이스를 받아 처리를 하기 때문에 getComputer() 에서 구현할 것이 복잡하지 않습니다.
  • 추상 팩토리 패턴은 추후에 sub class를 확장하는 데 있어 굉장히 쉽게할 수 있습니다.
    위 예에서 만약 Laptop 클래스를 추가하고자 한다면 getComputer()의 수정 없이 LaptopFactory만 작성해주면 됩니다.
    이러한 특징에 기반하여 추상 팩토리 패턴은 "Factory of Factories"라고도 불립니다.
  • 추상 팩토리 패턴 팩토리 패턴(팩토리 메소드 패턴)의 조건문(if-else, switch 등)으로부터 벗어납니다.

 


참고자료 : https://readystory.tistory.com/119

 

[생성 패턴] 추상 팩토리 패턴(Abstract Factory Pattern) 이해 및 예제

이번에 살펴볼 디자인 패턴은 생성 패턴 중 하나인 추상 팩토리 패턴(Abstract Factory Pattern)입니다. 추상 팩토리 패턴은 이전 포스팅에서 살펴본 팩토리 패턴(Factory Pattern)과 유사한 패턴으로, 팩토

readystory.tistory.com

                  https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

www.hanbit.co.kr

 

    전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴

     ==> 클래스의 인스턴스를 하나만 생성하여 사용하는 패턴(Ex DB Connection pool, 시스템 환경설정 등) 

            주의할점은 여러곳에서 동시에 접근해서 생길수 있는 문제(동기화문제)를 잘 파악하고 설계해야됨

     

 

1) 생성자 기본 패턴 예제 

      생성자를 private로 해서 외부에서 직접 못사용하게,  사용자에게 getInstance를 통해 생성되거나 얻을수 있게 함

package com.patterns.creationalFactory;

public class SingletonBasic {
	//싱글톤 객체를 static 변수로 선언
    private static SingletonBasic instance;
    private int msg;
    
    //외부에서 생성자 호출 막기
    private SingletonBasic(int msg) {
        this.msg = msg;
    }

    //인스턴스를 전달
    public static SingletonBasic getInstance(int msg) {
        if (instance == null) {
            instance = new SingletonBasic(msg);
        }
        return instance;
    }

    public void printMsg() {
        System.out.println(msg);
    }


//class Main {
    public static void main(String[] args) {
        SingletonBasic instance  = SingletonBasic.getInstance(1);
        SingletonBasic instance2 = SingletonBasic.getInstance(2);
        instance.printMsg();
        instance2.printMsg();
    }
}

※ Thread(동시성)문제 발생

package com.patterns.creationalFactory;

public class SingletonTreadProblem {
	private static SingletonTreadProblem instance;
    private int msg;

    private SingletonTreadProblem(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static SingletonTreadProblem getInstance(int msg) {
        if(instance == null) {
            instance = new SingletonTreadProblem(msg);
        }
        return instance;
    }

    public int getMsg() {
        return msg;
    }
}

class MainTreadProblem {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            SingletonTreadProblem singleton = SingletonTreadProblem.getInstance(num);
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

처리결과

 

2) 동시성 보완  : Eager initailization(이른 초기화, Thread safe)

     클래스 로더가 생성되는 시점에 메모리에 등록 즉 변수 선언과 동시에 초기화

package com.patterns.creationalFactory;

public class SingletonThreadSafe {
	//선언과 동시에 초기화
    private static SingletonThreadSafe instance = new SingletonThreadSafe(0);
    private int msg;

    private SingletonThreadSafe(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static SingletonThreadSafe getInstance() {
        return instance;
    }
    public int getMsg() {
        return msg;
    }
}
class MainThreadSafe {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            SingletonThreadSafe singleton = SingletonThreadSafe.getInstance();
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

 

3) 게으른 초기화1 : Lazy Initialization with synchronized(게으른 초기화, 동기화 블럭)

    동기화 블럭사용 : 근데 대기시간이 길다..

package com.patterns.creationalFactory;

public class SingletonSynchronized {
	private static SingletonSynchronized instance;
    private int msg;

    private SingletonSynchronized(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //synchronized 키워드 사용
    public static synchronized SingletonSynchronized getInstance(int msg) {
        if(instance == null) {
            instance = new SingletonSynchronized(msg);
        }
        return instance;
    }

    public int getMsg() {
        return msg;
    }
}

class MainSynchronized {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            SingletonSynchronized singleton = SingletonSynchronized.getInstance(num);
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

 

4) 게으른 초기화2 :  Lazy Initialization. Double Checking Locking(DCL)

      java에서 가장 늦은연산자는 new연산자이다 그러므로 생성시점이 아니라 메서드 내 호출시로 내리자 

package com.patterns.creationalFactory;

public class SingletonDCL {
	
	//Lazy Initialization. Double Checking Locking(DCL)
	
	private static SingletonDCL instance;
    private int msg;

    private SingletonDCL(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static SingletonDCL getInstance(int msg) {
        if (instance == null) {
            //instance가 null인 경우 synchronized 블록 접근
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL(msg);
                }
            }
        }
        return instance;
    }

    public int getMsg() {
        return msg;
    }
}

class MainDCL {
    public static int num = 1;

    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            SingletonDCL singleton = SingletonDCL.getInstance(num);
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

 

5) 게으른 초기화3 :  Lazy Initialization. LazyHolder

이 방법은 클래스안에 클래스를 두는 holder방법을 이용한 것이다.
아래 코드에서 중첩 클래스의 instance는 getInstance()가 호출되기 전까지는 초기화 되지않는다. 또한 instance는 static이므로 클래스 로딩 시점에 한번만 호출되고 final을 사용해 다시 값이 할당되지 않도록 함으로써 동시성 문제를 해결할 수 있다.

가장 성능이 좋고 많이 쓰이는 방식이다.

package com.patterns.creationalFactory;

public class SingletonLazyHolder {
	
	//Lazy Initialization. LazyHolder
	private static SingletonLazyHolder instance = new SingletonLazyHolder(0);
    private int msg;

    private SingletonLazyHolder(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //static 클래스안에 static 멤버 변수 선언 및 초기화
    private static class Initial {
        private static final SingletonLazyHolder instance = new SingletonLazyHolder(0);
    }
    public static SingletonLazyHolder getInstance() {
        return Initial.instance;
    }

    public int getMsg() {
        return msg;
    }
}

class MainLazyHolder {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            SingletonLazyHolder singleton = SingletonLazyHolder.getInstance();
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

 

 

참고자료 : https://cjw-awdsd.tistory.com/42

 

[디자인 패턴] Singleton Pattern 개념/예제

1. 싱글톤 패턴 싱글톤 패턴이란 클래스의 인스턴스를 하나만 생성하여 사용하는 패턴이다. 주로 특정 객체를 여러곳에서 공유해야 할 때 사용한다.(Ex: DB Conntection pool) 싱글톤 패턴을 이용함으

cjw-awdsd.tistory.com

                https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

www.hanbit.co.kr

 

ㅁ 소프트웨어 디자인 패턴이란?

소프트웨어 개발 방법으로 사용되는 디자인패턴(Desigin Pattern)은 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 그 방법에 이름을 붙여서 이후에 재사용하기 좋은 형태로 특정 규약을 만들어서 정리한 것입니다. 디자인 패턴은 소프트웨어 설계에 있어 공통적인 문제들에 대한 표준적인 해법과 작명법을 제안하며, 알고리즘과 같이 프로그램 코드로 바로 변환될 수 있는 형태는 아니지만, 특정한 상황에서 구조적인 문제를 해결하는 방식입니다. 즉 "효율적인 코드를 만들기 위한 방법론"이라고 생각하시면 됩니다.

디자인 패턴은 외우기보다는 어떠한 패턴이 있는지 알고 수많은 디자인 패턴에서 다양한 코딩 노하우를 습득하는것이 중요하다고 생각합니다. "이 코드에는 무조건 이 패턴을 적용시킬거야!" 이것이 아니라 여러가지 패턴이 자연스럽게 내 코드에 녹아드는것이 좋다고 생각합니다. 

 

     -  소프트웨어를 설계할 때 특정 맥락에서 자주 발생하는 고질적인 문제들이 또 발생했을 때

        ==> 재사용할 할 수있는 훌륭한 해결책
     - “바퀴를 다시 발명하지 마라(Don’t reinvent the wheel)”
         ==> 이미 만들어져서 잘 되는 것을 처음부터 다시 만들 필요가 없다는 의미이다.
      - 패턴이란
        각기 다른 소프트웨어 모듈이나 기능을 가진 다양한 응용 소프트웨어 시스템들을 개발할 때도 서로 간에 공통되는 설계 문제가 존재하며 이를 처리하는 해결책 사이에도 공통점이 있다. 이러한 유사점을 패턴이라 한다.

 

ㅁ 디자인 패턴의 종류

23가지의 디자인 패턴을 정리하고 3가지 범주로 분류함

1) 생성 패턴(Creational Patterns)

 객체 생성에 관련된 패턴입니다. 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공합니다.

종류

1. 싱글톤 패턴(Singleton) : 클래스의 인스턴스가 하나임을 보장하고 접근할 수 있는 전역적인 접근점을 제공하는 패턴으로, 디자인 패턴의 가장 많이 알려진 패턴입니다.    ==>  환경설정에 속성들이 해당되지 않을까 함

더보기

    private로 getInstance를 통하여 생성하도록 한다

   단 Thread(동시성)문제 발생 해결책으로 

           1)  Eager initailization(이른 초기화, Thread safe)

           2) 게으른 초기화1 : Lazy Initialization with synchronized(게으른 초기화, 동기화 블럭) :

                즉 getInstance에 synchronized 적용

           3) 게으른 초기화2 :  Lazy Initialization. Double Checking Locking(DCL)

                처리속도가 늦은 new연산자를 보안  getInstance안에 syschrnized 처리

           4) 게으른 초기화3 :  Lazy Initialization. LazyHolder

                getInstance에 Inital 메서드를 통해 final static 생성

                자세한 내용 : https://dicws.tistory.com/157

 

1.1 싱글턴(Singleton) :: 생성(Creational) 패턴

전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴  ==> 클래스의 인스턴스를 하나만 생성하여 사용하는 패턴(Ex DB Connection poo

dicws.tistory.com

2. 추상팩토리 패턴(Abstract Factory) : 구체적인 클래스를 지정하지 않고 관련성이 있거나, 독립적인 객체들을 생성하기  위한 인터페이스를 제공하는 패턴입니다.  

더보기

     -> 추상클래스에 추상화만들고 Consumer class를 처리하여,  팩토리 메서드 패턴의 if ~else if 를 걷어낼수 있다

                자세한 내용 : https://dicws.tistory.com/158?category=1060753 

 

1.2 추상 팩토리 패턴(Abstract Factory Pattern) ........생성(Creational) 패턴중

추상 팩토리 패턴은 생성 패턴(Creational Pattern) 중 하나이다. 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다. 생성 패턴에 속하는 패턴들은 객체를 생성, 합성하는 방법이나 객체

dicws.tistory.com

 

3. 팩토리 메서드 패턴(Factory Method) : 객체를 생성하는 인터페이스를 정의하지만, 인스턴스를 만드는 클래스는 서브클래스에서 결정하도록 하는 패턴입니다. 팩토리 메서드에서는 인스턴스를 만드는 것을 서브 클래스에서 하게 됩니다.

더보기

   -->   if ~else if 등으로   

   사용 예

  1. java.util 패키지에 있는 Calendar, ResourceBundle, NumberFormat 등의 클래스에서 정의된 getInstance() 메소드가 팩토리 패턴을 사용하고 있습니다.

Boolean, Integer, Long 등 Wrapper class 안에 정의된 valueOf() 메소드 또한 팩토리 패턴을 사용했습니다.

                자세한 내용  : https://dicws.tistory.com/159?category=1060753 

 

1.2 팩토리 메소드 패턴(Factory method pattern)........ 생성(Creational) 패턴중

팩토리 패턴은 생성 패턴(Creational Pattern) 중 하나이다. 또한 팩토리 메소드 패턴(Factory Method Pattern)이다 생성 패턴은 인스턴스를 만드는 절차를 추상화하는 패턴입니다. 생성 패턴에 속하는 패턴

dicws.tistory.com

4. 빌더 패턴(Builder) : 복학 객체의 생성과정과 표현과정을 분리시켜 동일한 생성과정에서 다양한 표현을 생성할 수 있는 패턴입니다.

더보기
        Computer comp = new Computer.ComputerBuilder("500 GB", "2 GB")
                .setBluetoothEnabled(true)
                .setGraphicsCardEnabled(true)
                .build();

    -- > 많은 Optional한 멤버 변수(혹은 파라미터)나 지속성 없는 상태 값들에 대해 처리해야 하는 문제들을 해결합니다.

                자세한 내용  : https://dicws.tistory.com/160?category=1060753 

 

1.4 빌더 패턴은 생성 패턴(Creational Pattern) ........ 생성(Creational) 패턴중

빌더 패턴은 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패

dicws.tistory.com

 

5. 원형 패턴(Prototype) : 생성할 객체의 종류를 명시하는 데 원형이 되는 예시물을 이용하고 새로운 객체를 이 원형들을 복사함으로써 생성하는 패턴입니다.

더보기

   clone()과 같이 복제하여 추가 자료 처리 

                자세한 내용  : https://dicws.tistory.com/161

 

1.5 프로토타입 패턴은 생성 패턴(Creational Pattern)........ 생성(Creational) 패턴중

프로토타입은 주로 실제 제품을 만들기에 앞서 대략적인 샘플 정도의 의미로 사용되는 단어입니다. 프로토타입 패턴은 객체를 생성하는 데 비용(시간과 자원)이 많이 들고, 비슷한 객체가 이미

dicws.tistory.com

 

2) 구조 패턴(Structural Patterns)

구조 패턴이란 작은 클래스들을 상속과 합성을 이용하여 더 큰 클래스를 생성하는 방법을 제공하는 패턴입니다.

이 패턴을 사용하면 서로 독립적으로 개발한 클래스 라이브러리를 마치 하나인 양 사용할 수 있습니다. 또, 여러 인터페이스를 합성(Composite)하여 서로 다른 인터페이스들의 통일된 추상을 제공합니다.

 

구조 패턴의 중요한 포인트는 인터페이스나 구현을 복합하는 것이 아니라 객체를 합성하는 방법을 제공한다는 것입니다. 이는 컴파일 단계에서가 아닌 런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖습니다.

 

종류

1. 적응자 패턴(Adapter or Wrapper) : 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴으로, 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해주는 패턴입니다.

더보기

 Class Adapter(Inheritance) Object Adapter(Composite) 두 가지 방법으로 호환되지 않은 클래스를 변환하는 패턴

  ex) 120v -> 3v -> 2v 등등 convertVolt를 통해 변환 함

                자세한 내용  : https://dicws.tistory.com/162

 

https://dicws.tistory.com/manage/newpost/162?type=post&returnURL=https%3A%2F%2Fdicws.tistory.com%2F162

 

dicws.tistory.com

2. 컴포지션 패턴(Composite) : 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴입니다. 사용자는 이 복합체 패턴을 통해 단일 객체와 복합 객체 모두 동일하게 다룰 수 있습니다.

더보기

그룹으로 묶어 한꺼번에 처리할수 있다.

 

복합체 패턴은 다음과 같은 오브젝트들을 갖습니다.

  • Base Component - 베이스 컴포넌트는 클라이언트가 composition 내의 오브젝트들을 다루기 위해 제공되는 인터페이스를 말합니다. 베이스 컴포넌트는 인터페이스 또는 추상 클래스로 정의되며 모든 오브젝트들에게 공통되는 메소드를 정의해야 합니다.
  • Leaf - composition 내 오브젝트들의 행동을 정의합니다. 이는 복합체를 구성하는 데 중요한 요소이며, 베이스 컴포넌트를 구현합니다. 그리고 Leaf는 다른 컴포넌트에 대해 참조를 가지면 안됩니다. 
  • Composite - Leaf 객체들로 이루어져 있으며 베이스 컴포넌트 내 명령들을 구현합니다.

                자세한 내용  : https://dicws.tistory.com/163?category=1060753 

 

2.2 컴포지트 패턴(Composite Pattern) .....구조 패턴(Structural Pattern) 중

Composite 패턴은 구조 패턴 중 하나로, 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴입니다. 사용자는 이 복합체 패턴을 통해 단일 객체와 복합 객체 모두 동일하게 다

dicws.tistory.com

 

3. 프록시 패턴(Proxy) : 어떤 다른 객체로 접근하는 것을 통제하기 위해 그 객체의 매니저 또는 자리 채움자를 제공하는 패턴입니다.

더보기

어떤 다른 객체로 접근하는 것을 통제하기 위해서 그 객체의 대리자(surrogate)나 자리표시자(placeholder)의 역할을 하는 객체를 제공하는 패턴입니다.  

또한 어떤 객체에 대하여 접근할 때에 Wrapper Class를 두어 접근에 대한 통제(Control access)를 위해 사용합니다.

                자세한 내용  : https://dicws.tistory.com/164?category=1060753 

 

2.3 프록시 패턴(Proxy Pattern) .....구조 패턴(Structural Pattern) 중

프록시 패턴(Proxy Pattern) 특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)을 제공합니다. 프록시 패턴은 디자인 패턴 중 이해하기 쉬운 패턴에 속합니다. 프록시 패턴은 구조

dicws.tistory.com

4. 플라이웨이트 패턴(Flyweight): '공유(Sharing)'를 통하여 대량의 객체들을 효과적으로 지원하는 방법

더보기

Shape interface를 구현하고 Line과 Oval 등 implements 해서 FlyweightFactory에 있으면 해당건 없으면 생성방식으로 공유해서 사용토록 한다  ->아이콘등쓰면 좋을듯

 

자바의 모든 래퍼 클래스의 valueOf() 메소드가 바로 이 플라이웨이트 패턴을 사용하고 있습니다. 그래서 래퍼 클래스를 생성해야 할 때 new 키워드를 통해 인스턴스를 매번 생성하기보다는 valueOf() 메소드를 통해 생성하는 것이 더 효율적입니다.

  ※ 참고:  Facade 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 많은 수의 객체를 생성할때 사용함

                자세한 내용  : https://dicws.tistory.com/165?category=1060753 

 

2.4 플라이웨이트 패턴(Flyweight Pattern) .....구조 패턴(Structural Pattern) 중

플라이웨이트 패턴(Flyweight Pattern)은 Facade 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 많은 수의 객체를 생성해야 할 때 사용하는 패턴입니다. 플라이웨이트 패턴 이해 및 예제 디자

dicws.tistory.com

 

5. 퍼사드 패턴(Facade) : 서브시스템에 있는 인터페이스 집합에 통합된 하나의 인터페이스를 제공합니다. 서브시스템을 좀 더 쉽게 사용하기 위해 고수준(higher-level)의 인터페이스를 정의합니다.

더보기

MySql 과 Oracle PDF 리포트 Helper class 예시, 하위를 숨기려는게 아님

※ 참고 : 퍼사드 패턴(Facade Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 클라이언트가 쉽게 시스템과 상호작용 할 수 있도록 도와주는 패턴

                자세한 내용 : https://dicws.tistory.com/166?category=1060753 

 

2.5 퍼사드 패턴(Facade Pattern) .....구조 패턴(Structural Pattern) 중

퍼사드 패턴(Facade Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 클라이언트가 쉽게 시스템과 상호작용 할 수 있도록 도와주는 패턴입니다. 퍼사드 패턴 이해 및 예제

dicws.tistory.com

 

6. 브리지 패턴(Bridge) : 구현부에 추상층을 분리하여 각자 독립적으로 변형할 수 있도록 하는 패턴입니다. - 컴포지트 패턴(Composite) : 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 / 복합객체 모두 동일하게 다루도록 하는 패턴입니다.

더보기

Shape와 Color  를 Triangle, Pentagon에 적용

※ 참고 :  Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 두 인터페이스에 계층 구조(Hierarchy)를 가지고 있을 때 인터페이스를 구현(implements)으로부터 분리하고 클라이언트 프로그램으로부터 구현 세부사항을 숨기기 위해 사용되는 패턴입니다.

                자세한 내용 : https://dicws.tistory.com/167?category=1060753 

 

2.6 브릿지 패턴(Bridge Pattern) .....구조 패턴(Structural Pattern) 중

브릿지 패턴(Bridge Pattern)은 Flyweight 패턴, Adapter 패턴, Decorator 패턴처럼 구조 패턴 중 하나로, 두 인터페이스에 계층 구조(Hierarchy)를 가지고 있을 때 인터페이스를 구현(implements)으로부터 분리하..

dicws.tistory.com

 

7. 데코레이터 패턴(Decorator) : 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능확장이 필요할 때 서브클래스 대신 쓸 수 있는 대안이 될 수 있습니다.

더보기

JDK 에서 FileReader, BufferedReader 등 IO 클래스에 사용되는 패턴

Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));

※ 참고 : 런타임에서 객체의 기능을 수정하는데 사용되는 패턴

               자세한 내용 : https://dicws.tistory.com/168?category=1060753 

 

2.7 데코레이터 패턴(Decorator Pattern) .....구조 패턴(Structural Pattern) 중

데코레이터 패턴(Decorator Pattern)은 Flyweight 패턴, Adapter 패턴, Bridge 패턴처럼 구조 패턴 중 하나로, 런타임에서 객체의 기능을 수정하는데 사용되는 패턴입니다. 데코레이터 패턴 이해 및 예제 데

dicws.tistory.com

 

3) 행위 패턴(Behavioral Patterns)

객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴입니다. 한 객체가 혼자 수행할 수 없는 작업을 여러개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는것에 중점을 두는 방식입니다.

 

종류

1. 템플릿 패턴(Template) : 객체의 연산에서 알고리즘의 뼈대만 정의하고, 나머지는 서브클래스에서 이루어지게 하는 패턴입니다. 템플릿패턴은 알고리즘의 구조는 변경하지 않고 알고리즘의 각 단계를 서브클래스에서 재정의하게 됩니다.

더보기

아이스 아메리카노, 라떼 -> 커피 :물끓인다 -> 에스프레소 넣는다->얼음넣는다-> 기타(우유/시럽)

※ 참고 : 템플릿 메소드 패턴은 "알고리즘의 뼈대"를 맞추는 것에 있다.

        즉, 전체적인 레이아웃을 통일 시키지만 상속받은 클래스로 하여금 어느정도 유연성을 주도록하는 디자인 패턴이다. 

               자세한 내용 :  https://dicws.tistory.com/171?category=1060753 

 

3.1 템플릿 메소드 패턴(Template Method Pattern) .....행동 패턴(Behavioral Pattern) 중

템플릿 메소드 패턴(Template Method Pattern) 알고리즘의 골격을 정의합니다. 템플릿 메소드를 사용하면 알고리즘 일부 단계를 서브클래스에서 구현할 수 있으며, 알고리즘의 구조는 그대로 유지하면

dicws.tistory.com

 

2. 옵저버 패턴(Observer) : 객체들 사이에 1 : N 의 의존관계를 정의하여 어떤 객체의 상태가 변할 때, 의존관계에 있는 모든 객체들이 통지받고 자동으로 갱신될 수 있게 만드는 패턴입니다.

더보기

Swing, Android 등에서 UI관련된 곳에서 이 옵저버 패턴이 많이 사용된다.

옵저버 패턴의 특징은 Loose Coupling을 잘 만족하여 서로 아는 지식은 적지만, 커뮤니케이션은 가능하다는 점이다.

 

예제) Observer 인터페이스에 update, Publisher 인터페이스에 regsiterObserver, removeObserver , notifyObservers

          Subscriber Publisher를 지정 , Observer 인터페이스의 update를 통해  갱신

               자세한 내용 :  https://dicws.tistory.com/172

 

3.2 옵저버 패턴(Observer Pattern) .....행동 패턴(Behavioral Pattern) 중

옵저버 패턴(Observer Pattern) 한 객체의 상태가바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.  즉, A 오브젝트

dicws.tistory.com

3. 이터레이터 패턴(Iterator,반복자) : 내부 표현부를 노출하지 않고 어떤 객체 집합의 원소들을 순차적으로 접근할 수 있는 방법을 제공하는 패턴입니다. 즉 일련의 순서를 가진 데이터 집합에 대하여 순차적인 접근을 지원하는 패턴이다.

더보기

Iterator 인터페이스에 hasNext, next, remove 메소드로 CoffeeMenuIterator를 구현 Collection Object를 생성하고,

 Aggregate(골재)에 abstract createIterator 메소드로 CoffeeMenu를 구현 CoffeeMenuIterator 인스턴스 생성보관

※ 참고 :  반복자 패턴 이터레이터 패턴이라고도 하고 Cursor라고도 한다.

     즉, 대상으로 삼는 Context는 객체를 여러개 담고 있는 Aggregate Object(Collection Obj)이다.(ex : ArrayList)

     -  Aggregate : 골재

               자세한 내용 :  https://dicws.tistory.com/173?category=1060753 

 

3.3 반복자 패턴(Iterator Pattern) .....행동 패턴(Behavioral Pattern) 중

반복자 패턴(Iterator Pattern) 반복자 패턴은 이터레이터 패턴이라고도 하고 Cursor라고도 한다. 즉, 대상으로 삼는 Context는 객체를 여러개 담고 있는 Aggregate Object(Collection Obj)이다.(ex : ArrayList..

dicws.tistory.com

4. 미디에이터 패턴(Mediator, 중재자) : 한 집합에 속해있는 객체들의 상호 작용을 캡슐화하는 객체를 정의하는 패턴입니다. 중재자는 객체들이 직접 서로 참조하지 않도록함으로써 객체들간의 느슨한 연결을 촉진시키며 객체들의 상호작용을 독립적으로 다양화 시킬 수 있도록 해줍니다. 

더보기

옵져버는 Substiber가 받기만 하는데 Mediator는 서로 통신한다

관제탑은  ISource Mediator, event발생에 대한 선언 인터페이스로 Tcpcom, SystemSignal 구현

Mediator 클래스로 event를 받을 Destination을 추가 관리 event발생 notiy를 함

각 비행기는 IDestionation receiveEvent 선언 Display, Log 를 구현함으로 TcpCom과 SystemSingnal에 발생된 이벤트를 전달한다.

※ 참고 : M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용의 확장성에 유리한 패턴이다.M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용의 확장성에 유리한 패턴이다.

               자세한 내용 : https://dicws.tistory.com/174

 

3.4 중재자 패턴(Mediator Pattern).....행동 패턴(Behavioral Pattern) 중

모든 클래스간의 복잡한 로직(상호작용)을 캡슐화하여 하나의 클래스에 위임하여 처리하는 패턴이다. 즉, M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용의 확장성에 유리

dicws.tistory.com

 

5. 스트레이트지 패턴(Strategy, 전략) : 동일 계열의 알고리즘들을 정의하고, 각각 캡슐화하며 이들을 상호교환 가능하도록 만드는 것입니다. 알고리즘을 사용하는 사용자로부터 독립적으로 알고리즘이 변경될 수 있도록 하는 패턴입니다.

더보기

코드에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다. (달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 "캡술화")
상속보다는 구성을 활용한다.(상속 보다는 객체의 합성을 중요시 하자)
상속보다는 구성을 활용한다. 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다.

 

예제는 오리게임을 통해 날고있는행동, 울고있는 행동을 분리

 

※ 참고 : 즉, 행위를 클래스로 캡슐화 하여 동적으로 행위를 바꾸어도 코드가 복잡해 지지 않도록 한다.
여기서의 패턴 이름과 같이 전략을 쉽게 바꾸도록 해주는 디자인 패턴이다.

               자세한 내용 : https://dicws.tistory.com/175

 

3.5 전략 패턴(Stragety Pattern).....행동 패턴(Behavioral Pattern) 중

스트래티지 패턴(Strategy pattern)은 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

dicws.tistory.com

 

6. 상태 패턴(State) : 객체의 내부 상태가 변경될 때 행동을 변경하도록 허락합니다. 객체는 자신의 클래스가 변경되는 것처럼 보이게 됩니다.

더보기

HandState 인터페이스를 통해 하위에 Rock, Scissors, Paper등의 행동을 구체화하여 Person의 상태를 set, get을 통해 자신이 변경한것처럼 한다.

※ 참고 :  상태 패턴은 내부 상태 객체들이 캡슐화되어 외부에서 전혀 알 수 없게되고,

              외부에서는 Context 객체의 각 Action들을 호출하기만 할 뿐이다.

              이를 통해 Decoupling을 하여 코드의 수정 및 확장을 용이하게 한다.

               자세한 내용 : 

 

7. 책임연쇄 패턴(Chain of Responsibility,역할 사슬) : 명령 객체와 일련의 처리 객체를 포함하는 디자인 패턴이다. 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이고, 체인 안의 처리 객체가 핸들할 수 없는 명령은 다음 처리 객체로 넘겨진다.

더보기
  1. Handler
    요청을 수신하고 처리객체들의 집합에 전달하는 인터페이스이다.
    집합의 첫 번째 핸들러에 대한 정보만 가지고 있으며 그 이후의 핸들러에 대해서는 알 수 없다.
  2. Concrete Handler
    요청을 처리하는 실제 처리객체입니다.
  3. Client
    요청을 전달하는 클라이언트입니다.

동전 거슬러주기 큰단위부터 작은단위로 위임하면서 처리 예시

분배체인 인터페이스(DispenseChain) 통해 Won100Dispenser - > Won10Dispenser -> Won1Dispenser로 넘김

※ 참고 : 어떤 요청이 그 요청을 담당하는 객체에 들어오면 각각의 요청에 대해서 특정한 객체가 담당하는 것이 일반적이지만 객체를 연결리스트와 같은 사슬 방식으로 연결한 후에 요청을 수행하지 못하는 객체라면 다음 객체에 넘기며 책임을 넘기는 형태의 패턴을 말한다.  예) Try catch 문이 대표

               자세한 내용 : https://dicws.tistory.com/177?category=1060753 

 

3.7 책임연쇄 패턴(Chain of responsibility) .....행동 패턴(Behavioral Pattern) 중

정의 명령 객체와 일련의 처리 객체를 포함하는 디자인 패턴이다. 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이고, 체인 안의 처리 객체가 핸들할 수 없는 명령은 다음 처리 객

dicws.tistory.com

 

8. 메멘토 패턴(Memento) : 메멘토 패턴은 객체의 상태 정보를 저장하고 사용자의 필요에 의하여 원하는 시점의 데이터를 복원 할 수 있는 패턴을 의미합니다.

더보기

Information 클래스에 실제 데이터넣고  CreateMemento 메서드를 통해 복원시점 Memento 클래스 넣고

CareTaker stack함수를 통해  push, pop을 통하여 넣고 빼게 되어 있음

               자세한 내용 :  https://dicws.tistory.com/179

 

3.8 메멘토 패턴 (Memento Pattern) .....행동 패턴(Behavioral Pattern) 중

메멘토 패턴은 객체의 상태 정보를 저장하고 사용자의 필요에 의하여 원하는 시점의 데이터를 복원 할 수 있는 패턴을 의미합니다. ■메멘토 패턴 예제 구조 ▶ 실제로 메멘토 패턴을 사용하

dicws.tistory.com

9. 비지터 패턴(Visitor) : 객체구조를 이루는 원소에 대해 수행할 연산을 표현합니다. 방문자는 연산에 적용할 원소의 클래스를 변경하지 않고 새로운 연산을 재정의 할 수 있습니다.

※ 참고 : 알고리즘을 객체 구조에서 분리시키는 디자인 패턴이다.

               자세한 내용 :  https://dicws.tistory.com/180

 

3.9 비지터 패턴(Vistor Pattern) .....행동 패턴(Behavioral Pattern) 중

Visitor는 사전적인 의미로 어떤 장소에 찾아오는 사람이라는 의미를 갖고 있다. 방문자 패턴에서는 데이터 구조와 처리를 분리한다. 데이터 구조 안을 돌아다니는 주체인 방문자를 나타내는 클

dicws.tistory.com

10. 인터프리터 패턴(Interpreter) : 주어진 언어에 대해서 문법을 위한 표현수단을 정의하고, 해당 언어로 된 문장을 해석하는 해석기를 사용하는 패턴입니다.

 

※  참고 : 문법적 규칙을 클래스화 하여, 일련의 규칙을 통해 언어/문법을 해석하는 패턴

 

               자세한 내용 : https://dicws.tistory.com/181

 

3.10 인터프리터 패턴(Interpreter Pattern) .....행동 패턴(Behavioral Pattern) 중

문법적 규칙을 클래스화 하여, 일련의 규칙을 통해 언어/문법을 해석하는 패턴이다.  사용되는 곳 ​ SQL 문은 SELECT (어쩌구) FROM (저쩌구) WHERE (이러쿵저러쿵) 과 같이 ​ 특정 문법을 지키면 MYSQL

dicws.tistory.com

11. 커맨드 패턴(Command) : 요청을 객체로 캡슐화하여 서로 다른 사용자의 매개변수화, 요청 저장 또는 로깅, 연산의 취소를 지원하게 만드는 패턴입니다.

 

※ 참고 : 실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와 실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거한다.

따라서 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용 할 수 있도록 해준다.

               자세한 내용 : https://dicws.tistory.com/182

 

3.11 커맨드 패턴(Command) .....행동 패턴(Behavioral Pattern) 중

실행될 기능을 캡슐화함으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴 즉, 이벤트가 발생했을 때 실행될 기능이 다양하면서도 변경이 필요한 경우

dicws.tistory.com

 

 

 

 

참고 원문  : https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8616098823 

 

[Design pattern] 많이 쓰는 14가지 핵심 GoF 디자인 패턴의 종류

디자인 패턴을 활용하면 단지 코드만 ‘재사용’하는 것이 아니라, 더 큰 그림을 그리기 위한 디자인도 재사용할 수 있습니다. 우리가 일상적으로 접하는 문제 중 상당수는 다른 많은 이들이 접

www.hanbit.co.kr

                    https://coding-factory.tistory.com/708

 

[Design Pattern] 디자인 패턴 정의와 종류에 대하여

소프트웨어 디자인 패턴이란? 소프트웨어 개발 방법으로 사용되는 디자인패턴(Desigin Pattern)은 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 그 방법에 이름을 붙여서 이

coding-factory.tistory.com

 

버전별 plugin이 설치 안되는것도 있고,  몇일 삽질을 하다 보니 마지막에 설치된 내용을 정리하고자 합니다.

 

내가 사용하는 Plugin 설치내용 

 - WindowBuilder           

    RCP 설치시 화면 디자인을 하기위해 꼭 필수 Plugin

- PyThon :

- Bubel : 이클립스 개발 툴 한글버전 설치

- UML 관련 두가지 설치

    ObjectAid : 기존 개발된 클래스를 끌어놓으면 Class Diagram을 쉽고 빠르게 보여줌

                      단점은 Sequence Diagram은 라이센스를 받아야되는데 받는곳이 없어진듯

                   ※ 개발할때 좋은데 설치가 까다로움 이것때문에 삽질을 좀 많이 한듯,

                       상위버전에서 안된다고들 했는데 그것도 아닌듯==> 설치하다 포기함

    AmaterasUML : 이것도 UML을 작성하기 위해 쓰는 Plugin

 

이클립스 설치

    다운로드 경로 : https://www.eclipse.org/downloads/packages/release/2020-12/r

    ※ 단순다운받고 압축만 풀어주면 되고 사전에 Java Jdk가 설치 및 환경설정이 되어 있으면 된다

    ※ 이것저것 하다 결국 2020-12R버전으로 설치하니 모든게 정상인듯

 

Eclipse Packages | The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 350 open source pro

495 MB 287,239 DOWNLOADS Tools for developers working with Java and Web applications, including a Java IDE, tools for JavaScript, TypeScript, JavaServer Pages and Faces, Yaml, Markdown, Web Services, JPA and Data Tools, Maven and Gradle, Git, and more. Cli

www.eclipse.org

   - Java Editon : 사용처에 따른 Package

     SE : Standard Editon -클라이언트 지향의 기본적인 환경

     EE :  Enterprise Editon - 서버 소프트웨어 개발을 위한 환경

     ME : Micro Editon- PDA나 휴대전화등  소형기기를 위한 소프트웨어 개발환경 

이클립스 종류에 정리

   - 버전별로 2020년도 버전

이클립스 버전

 - 버전안에서도 추가적으로 바뀐것에 따라 종류가 나누어짐

    R : Release  - 안정화된

    RC1 : Release Condidate1 - 안정화가 될 후보 버전

    M1, M2, M3: Milestone - 개발 중인 버전

 

 

1) 이클립스 설치 과정

     과정생략 : 하위폴더에 eclipse.exe를  실행하면 구동됨

 

2) WindowBuilder 설치 

    요즘은 Install보다 Eclipse Marketplace를 주로 사용하는듯 내용은 비슷함

 

Eclipse Marketplace

          windowbuilder 검색후 설치

WindowBuilder 설치

       확인방법 : New -> Other로 wizard에서 WindowBuilder가 나오면 됨

 

 ※ 특정버전에 WindowBuilder가 설치해도 메뉴가 안나오는게 있는데 잘 모르겠음

 

3) PyThon 

    마찬가지로  Eclipse Marketplace  pydev  검색후 설치 , 단 python 추가 별도 설치는 설명 생략

설치후라 insalled 나옴

     - python 추가설정   

  - python Undefined variable from import: randn 이런류의 에러발생시

    Window -> Preferences -> PyDev -> Editor -> Code Analysis -> Undefined -> Undefined Variable From Import -> Ignore  체크 제외

 

4) Bubel

    이클립스 개발 툴 한글버전 설치

    주소 : https://download.eclipse.org/technology/babel/update-site/latest/

     Install New Software로 설치

 

  

5) UML 관련 두가지 설치

    a) ObjectAid 설치 과정 : objectaid-1.1.14.zip 파일을 구해서 종전 Install 방법인 Achive 설치

     

Install New Software 처리메뉴

      - 확인방법 

주의 : 여기서 마지막에 파일명을 넣고 Finish가 안되면 파일도 생성되지 않고 해당 버전에 사용할수 없음

          파일이 생성후 연관 class를 옮겨놓으면 클래스 다이어 그램이 만들어짐

  

 

    b) AmaterasUML 설치과정

      먼저 GEF 부터 설치

      Install에 주소 : http://download.eclipse.org/tools/gef/updates/releases/

 

Eclipse software repository | The Eclipse Foundation

The Eclipse Foundation - home to a global community, the Eclipse IDE, Jakarta EE and over 360 open source projects, including runtimes, tools and frameworks.

download.eclipse.org

 

  AmaterasUML_1.3.4.zip 다운로드 파일을 eclipse폴더 plugin 추가 또는 

다운로드 주소 :  http://takezoe.github.io/amateras-update-site

 

 

기본적으로 jre는 설치됬다 보고 이클립스 설치

 

근데 구버전의 경우 이클립스 환경설정 및 프로젝트에 파이쎤 추가가 안되는 현상이 있었음

내가 잘 못하는건지 아니면 뭐가 잘못된건지 몰라서 신버전으로 파이쎤은 사용하기로 했음

 

Requirements : 이거 안보면 헤메인다.

  • Java 8: Important: If you don't have java 8, the update process may appear to succeed, but PyDev will simply not show in the target installation. See PyDev does not appear after install! below for details on how to fix that.

At least one of:

and

 

살펴보니 컴퓨터의 java 버전이 1.7이었음. ㅠ

jdk 버전을 1.8 로 올린 후, 환경 변수 바꿔주고, 이클립스를 재실행해보니

이제사 windows -> preference 에서 PyDev 메뉴가 노출된다.

 

(현재 버전은 2022-03 버전)

 

Help -> Eclipse Marketplace 

 

pydev 검색하고 install

  ※ 구버전 특히 2018-12-R버전 이하에서는 파이쎤 메뉴가 나오지 않는데 잘 모르겠음.

  ※ 퍼스펙티브 정상으로 보이고 프로젝트 정상으로 생김 확인

 

ㅁ 파이쎤 환경설정

   

 

 

'IT > Python' 카테고리의 다른 글

파이쎤 이클립스 버전별 설치  (0) 2022.09.04
Python 포트블 환경설정(embeddable)  (0) 2022.07.31

Python 설치  https://www.python.org/downloads/windows/

 

1) 위 링크 하단에 수동설치버전 다운로드

포터블 설치버전

 

python 실행하여 정상 확인

 pip 설치 하단 주소 마우스 오른쪽으로 다른이름 저장하여 python 기본폴더에 저장

   https://bootstrap.pypa.io/get-pip.py 

 

python get-pip.py 실행

==> 기본 Lib, Script 폴더 생성됨

※ "Scripts" 폴더.   pip 명령어가 추가 생성됨

 

pip환경설정

  기본 경로에 python버전명._pth 파일을 울트라 수정

ㅁ Scripts 폴더에 pip명령어로 beautifulsoup4 설치

      ==> Scripts폴더가서 pip install bs4 로 설치

 

ㅁ 경로 설정

     윈도우 검색에 고급 시스템 

환경변수 설정

 

환경변수 추가

파이쎤 설치 포터블 버전 완료

 

ㅁ Qt Designer 설치

     PySide2를 설치 하면 자동 설치됨

 

'IT > Python' 카테고리의 다른 글

파이쎤 이클립스 버전별 설치  (0) 2022.09.04
python 이클립스 설치  (0) 2022.07.31

1. 스레드(Thread)

1. 기본개념

1.1 용어

    Tread의 용어는 '실'   -> 일단 떠올라야되는게 동시실행이다

    기본개념확인

    - 프로그램 : 하드 디스크상에 저장된 소프트웨어

    - 프로세스 : 메모리에 들어가 실행되는 프로그램

    - 스레드 : 실행중인 프로세스의 작업(단위) 단위

 

 하나의 실행 main 스레드라 부른다.

main 스레드의 처리흐름

            main() -> a호출 -> a실행 -> b호출 -> b실행 -> b종료-> a복귀 -> a종료 -> main복귀 -> main종료

1.2 자바 메모리의 구성

 

1.2.1 메서드 영역(Method Area)

          static 변수와 메서의 바이트 코드가 들어간다.  스레드가 공유된다 

1.2.2 스택영역(Stack Area)

         지역변수가 저장된다. 스레드가 공유안된다.

1.2.3 힙영역(Heap Area)

          인스턴스가 저장이 된다. 이영역은 가지지 컬렉션의 대상이된다. 힙은 각 스레드가공유한다.

 

2. 스레드

  2.1 기본내용

         실의 길이에 따라 수행시간 그치만 빠르게 오답 또는 굉장히 느리게 정답을 출력함

          start는 코딩상이 아니라 동시 실행 -> cpu를 선점한다 어떤놈이 먼저인지 모름

         실행순서가 모름 즉 개발자입장에서 제어가 안된다 그래서 프로그램이 아니다?

          getName() -> 가상머신이 이름을 가져온다

         sleep을 통해 살짝 제운다 안그러면 안놓는다. ->말로만동시? 형광등 깜박임

  2.2 문법

        - Thread 클래스를 상속받는 방법

        -  Runnable 인터페이스를 구현

  2.2.1 Thread 클래스의 상속

            Thread를 상속받아 run() 실행 -> run method가 스레드의 main이다

 

  2.2.2 Runnerable 인터페이스 구현

           Runnable인터페이스는  생성하고 Thread를 생성후 Thread의 run메서드를 실행

  2.2.3 라이프 사이클

더보기

 참고)

     java1.0부터 있었음

     동시실행 1core에서는 multi Thread 는 안된다.

     패러럴 컴퓨터

3. 동기화(Synchronized)

3.1 동기화가 필요한경우(부제 : 굉장히 빠르게 오답이 나오는경우)

        3.1.1 스레드가 공유하는 메모리

                  메모리 영영중 메서드, 힙영역을 공유

                  동시에 실행되는 스레드가 공유되는 인스턴스가 가지고 있는 변수에 동시에 접근하는 상황이 발생된는데  이상황이 잘못된 결과를 만들수 있다

메모리 3개(큰방) : 메서드(static), 스택(로칼 -모든 프로그램에 다 있고 반환됨), 힙(인스턴스- 데이터만)

※ 스레드에서 힙에 올라간 데이터를 공유를 하는데 이게 문제

※ join메서드는 같이 가는게 문제 아니라 같이 끝난다

3.2 동기하기반 메서드(부제:굉장히 느리게 정답이 나오는경우)

       3.2.1 동기화메서드 & synchronized 기반 동기화 블록

         synchronized 이거 쓰면 된다 근데 늦다 => synchronized 기반 동기화 블록

  

      3.3.2 스레드의 블록킹 단위

                 메서드 만이 아니라 메서드가 속해있는 인스턴스 전체가 블록킹 된다. 

                synchronized : 블록이라 생각하자  누가 잡으면 못들어감

               모든 인스턴스는 열쇠? 스레드가 열쇠를 잡는 아이가 먼저 -> instance전체 

                =>결론 한줄서기임   

3.3 개발자가 개입하는 동기화 기반 코딩기법

         상황 T1이 변수 a를 변하는 동기화기반 메서드 m1,m2

                 T2가 변수 b를 변하는 동기화기반 메서드 m3,m4 인경우 

              각 인스턴스는 키를 하나씩 가지고 있다

              실행중인 스레드는 인스턴스에 접근할때 키를 가지고 접근한다.

 

              메서드안에는 동기화에 된 코드를 적을수 있는데 메서드 전체가 아니라 동기화 관련된것만 건다

             열쇠는 Object로 생성

 

 누가먼저는 모르지만 결과는 똑같이 하자!!! 그리고 컨트롤을 하자!!(우선순위가 무조건 앞뒤가 있을수 있다)

 

4. 순서제어

   4.1 순서 문제에 대한 해결책 

            각각 다른기능의 thread를 제어해보자 

            Car Class에서 array로 제어

            파는놈 사는놈 두개의 thread를  => 모든 스레드는 순서가 있고 제어하자

 

            wait(); 실행중인 스레드를 blocking (일단쉬어) <-> sleep  특정시간동안 (알람이 없음)

            notify() : blocking 메서드 하나만 실행

            notifyAll() : blocking 메서드가 실행

 

          ocjp -> ocjt(활주로 문제, 알파고 등등 스레드 필수)   -- OS

        

2.  스트림(Stream)과 File I/O

       Stream 강이아니라 물길정도 의미 : 데이터를 흐름 물길

       이번장은 코드가 바뀜

 

    - 자바는 입출력을 입출력 모델로 정의한다 -> 코드가 변한다

    - 모든 스트림은 단방향이다

    - 크게 'Byte Stream' 과 Char Stream 있다  => super class가 4개임(입/출력 분리)

    - 반드시 IOException

 

              Byte Stream   Char Stream

최상위   InputStream     Reader

              OutputStream  Writer

메서드는 read(), write()

 

생성자는 xxx최상위 클래스

 

EOF 상수이고 -1이다

 

 

표준입출력은 개발자가 건드리지 않는다(키보드/마우스)  c에 f.open  -> f는 format이다  point로 열기때문에 

여기서는 스트림이 주인공이다 FIle은 도구이다

 

FileCopy 1byte로 조각낼때 짤림 누수

 

 

1. 기본개념

     -1 은 EOF 상수값이다

 

1.1 용어

2. 바이트 스트림(Byte Stream)

2.1 기본개념

       - 1 바이트 단위로 움직이므로 상당히 느리다

       - 1 바이트 단위 움직이므로 데이터 깨짐 발생한다(소수,SizeOverFlow) 

2.2 데이터 전송 속도가 느려지는 문제

       문제를 처리하는 필터를 끼운다 FileInputStream - > BufferedInputStream  

2.3 데이터가 깨지는 문제

      DataInputStream DataOutputStream

      추가적으로 type을 알려줘야됨 : wrtie -> writeInt wrtieDouble 등등

    ※ Java는 필터를 여러개 쓸수있다

      DataOutputStream out=new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt")));

                                                      안깨지고                          빠르고

      필터를 끼우는 순서는?  생성자안에 받을수 있는놈이 있는지 확인한다(즉 생성자를 열어봐야된다)

 

참고)   java이후는 SizeOverFlow 발생된 데이터 순환된다  => C는 일부만

더보기

package day4.bytestream3;

public class SizeOverflow {
    public static void main(String[] args) {
        int a = 0;    // byte로 변경시  결과가 다름   기본과정 첫날  기본사이즈
        for (int i = 0; i < 300; i++) {
               System.out.println(a++);
        }
      }
}

2.4 최종 해결방법

 

3. 캐릭터 스트림(Character Stream)

3.1 등장이유

    -  자바 가상머신도 운영체제 앞에서는 하나의 응용프로그램이다

    - 모든 운영체제가 같은 코드를 사용하지 않는다

 

      java 문자는 2byte 유니코드인데 write할때 아스키로 변환(1byte)까지 해준다.

     xml은 3byte, 언어마다 코드가 다름 또한 운영체제도 친화적이지 않음

     운영체계에 따라 .. 해준다

 

3.2 Bufferedreader & BufferedWriter

      BufferedWriter  out.newLine()  메서드  

     

3.3 PrintWriter (Char Stream 이다)

      PrintWriter        out.println(""),  print, printf  메서드   ->서블릿에서 찍을때도 쓴다.

    ※ Sysout.out.println이것은 ByteStream 이다

 

4. 사용자 정의 필터 클래스

4.1 기본개념

4.2 사용방법

      내가 만든 필터 마지막임(슈퍼클래스가 됨)

      FilterInputStream 상속

      read()

      new String()    -> Serialization에 참여됨

      서버코드  <-> 클라이언트                                   =>   네트워크      Java에서는 인스턴스 직열화     

5. 객체 직렬화(Serialization)

   VO 클래스로 처리해볼까?

5.1 기본개념

     둘다 공통 약점 Byte Char  -> 데이터이동시 VO로 보낼시(1개씩이 아니라) 객체 직렬화

5.2 직렬화 대상제외

    보안상 특정항목을 지울때 transient -> 다른값으로 변경 불가

 

5.3Externalizable 인터페이스

5.3.1 writeExternal()

5.3.2 readExternal()

 

 

+ Recent posts