이번 포스팅에서는 디자인 패턴 정리 Java를 이용한 디자인 패턴 종류 정리를 하려고 합니다. 바로 시작하겠습니다.
2020/07/30 - [IT/Java] - [디자인 패턴] Java를 이용한 소스로 보는 디자인 패턴 종류 정리 1편
이전 편에 이어서 2편입니다. 이전편에서는 스트레터지 패턴, 어댑터 패던, 템플릿 메소트 패턴, 팩토리 메소드 패턴, 싱글톤 패턴, 프로토타입 패턴, 빌더 패턴에 대해서 정리했습니다.
추상 팩토리 패턴 Abstract Factory Pattern(객체 생성의 가상화)
추상 팩토리 패턴은 다양한 구성 요소 별로 '객체의 집합'을 생성해야 할 때 유용하다. 이 패턴을 사용하여 상황에 알맞은 객체를 생성할 수 있다.
- 관련 있는 객체의 생성을 가상화 할 수 있다.
- 생성 부분의 가상화 /관련있는 객체
public class AbstractFactoryPattern {
public static void main(String[] args) {
BikeFactory factory = new Samfactory();
Body body = factory.craeteBody();
Wheel wheel = factory.createWheel();
System.out.println(body.getClass());
System.out.println(wheel.getClass());
BikeFactory factory1 =new GtBikeFactory();
Body body1 = factory1.craeteBody();
Wheel wheel1 = factory1.createWheel();
System.out.println(body1.getClass());
System.out.println(wheel1.getClass());
}
}
interface Body{
}
interface Wheel{
}
interface BikeFactory{
public Body craeteBody();
public Wheel createWheel();
}
class Samfactory implements BikeFactory{
@Override
public Body craeteBody() {
// TODO Auto-generated method stub
return new SamBody();
}
@Override
public Wheel createWheel() {
// TODO Auto-generated method stub
return new SamWheel();
}
}
class SamBody implements Body{
}
class SamWheel implements Wheel{
}
class GtBody implements Body{
}
class GtWhell implements Wheel{
}
class GtBikeFactory implements BikeFactory{
@Override
public Body craeteBody() {
// TODO Auto-generated method stub
return new GtBody();
}
@Override
public Wheel createWheel() {
// TODO Auto-generated method stub
return new GtWhell();
}
}
또다른 예시
public class AbstractFactoryPattern_2 {
public static void main(String[] args) {
GuiFac fac = new LinuxGuiFac();
TextArea text = fac.createTextArea();
Button button = fac.craeteButton();
System.out.println(text.getText() + button.getButton());
GuiFac win = new WindowFac();
TextArea text1 = win.createTextArea();
Button button1 = win.craeteButton();
System.out.println(text1.getText() + button1.getButton());
//맥도 같은 패턴으로 가능
// 사용자는 따로 소환안해도 되도록 만들기
GuiFac fac2 = FactoryInstence.getGuiFac();
}
}
interface GuiFac{
public Button craeteButton();
public TextArea createTextArea();
}
interface TextArea{
public String getText();
}
interface Button{
public String getButton();
}
// 패키지 Window
class WindowFac implements GuiFac{
@Override
public Button craeteButton() {
// TODO Auto-generated method stub
return new WindowButton();
}
@Override
public TextArea createTextArea() {
// TODO Auto-generated method stub
return new WindowTextAarea();
}
}
class WindowButton implements Button{
@Override
public String getButton() {
// TODO Auto-generated method stub
return "윈도우 버튼";
}
}
class WindowTextAarea implements TextArea{
@Override
public String getText() {
// TODO Auto-generated method stub
return "윈도우 텍스트";
}
}
// 패키지 Linux
class LinuxGuiFac implements GuiFac{
@Override
public Button craeteButton() {
//리눅스 소스
return new LinuxButton();
}
@Override
public TextArea createTextArea() {
// TODO Auto-generated method stub
return new LinuxTextArea();
}
}
class LinuxButton implements Button{
@Override
public String getButton() {
// TODO Auto-generated method stub
return "리눅스 버튼";
}
}
class LinuxTextArea implements TextArea{
@Override
public String getText() {
// TODO Auto-generated method stub
return "리눅스 text";
}
}
// 패키지 Mac .... 두개와 같은 방식으로 사용간으
// 콘크리트 패키지 외부에 유출되면안됨
class FactoryInstence {
public static GuiFac getGuiFac(){
switch(0){
case 0 : return new WindowFac();
case 1 : return new LinuxGuiFac();
//case 2 : return Mac factory ...
}//구현된 내용을 알 수 없음
return null;
}
}
브리지 패턴 (Bridge Pattern)
브리지 패턴이란 구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴이다.
public class BridgePattern {
public static void main(String[] args) {
// 어댑터 패턴과 브릿지 패턴을 연결하여 이해 기는부분과 구현 부분을 분리 (어댑터패턴과흡사)
//모스부호 를 구현
PrintMorseCode code = new PrintMorseCode(new DefaultMSF());
code.g().a().r().a().m();
}
}
//필요할대마다 기능만넣어주는 클래스 생성 기계에 넣어주는 코드 등
class DefaultMSF implements MorseCodeFunction{
@Override
public void dot() {
// TODO Auto-generated method stub
System.out.print(".");
}
@Override
public void dash() {
// TODO Auto-generated method stub
System.out.print("-");
}
@Override
public void space() {
// TODO Auto-generated method stub
System.out.println(" ");
}
}
interface MorseCodeFunction{
public void dot();
public void dash();
public void space();
}
class MorseCode{ //수정시마다 이코드로와서 구현을 바꿔줘야함
private MorseCodeFunction function;
public void dot(){
function.dot();
// System.out.print(".");
// System.call.Bip(); 구현을 바꿀대마다 바꿔야하기때문에 힘듬 그래서 인터페이스필요
}
public void dash(){
//델리게이트패턴 다른대로 위임함
function.dash();
//System.out.print("-");
// System.call.longBip();
}
public void space(){
function.space();
// System.out.print(" ");
// System.call.longTip();
}
public MorseCodeFunction getFunction() {
return function;
}
public void setFunction(MorseCodeFunction function) {
this.function = function;
}
}
class PrintMorseCode extends MorseCode{
public PrintMorseCode g(){
dash();dash();dot();space();
return this;
}
public PrintMorseCode a(){
dot();dash();space();
return this;
}
public PrintMorseCode r(){
dot();dash();dot();space();
return this;
}
public PrintMorseCode m(){
dash();dash();space();
return this;
}
}
컴포지트 패턴 (Composite Pattern)
컴포지트 패턴이란 객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다
import java.util.ArrayList;
import java.util.List;
public class CompositePattern {
public static void main(String[] args) {
Folder root = new Folder("root"), home = new Folder("home"), garam = new Folder("garam"),
music = new Folder("music"), picture = new Folder("picture"), doc = new Folder("doc"),
usr = new Folder("usr");
File track1 = new File("track1"), track2 = new File("track2"), pic1 = new File("pic1"), doc1 = new File("doc1"),
java = new File("java");
// 폴더 파일시스템 처럼 집어넣기
root.addComponent(home);
home.addComponent(garam);
garam.addComponent(music);
music.addComponent(track1);
music.addComponent(track2);
garam.addComponent(picture);
picture.addComponent(pic1);
garam.addComponent(doc);
doc.addComponent(doc1);
root.addComponent(usr);
usr.addComponent(java);
show(root);
}
// 확인
private static void show(Component component) {
System.out.println(component.getClass().getName() + "|"+component.getName());
if (component instanceof Folder) {
for (Component c : ((Folder) component).getChilderen()) {
show(c);
}
}
}
}
abstract class Component {
private String name;
public Component(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class File extends Component {
public File(String name) {
super(name);
}
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
class Folder extends Component {
public Folder(String name) {
super(name);
}
List<Component> childeren = new ArrayList<>();
public List<Component> getChilderen() {
return childeren;
}
public void setChilderen(List<Component> childeren) {
this.childeren = childeren;
}
public boolean addComponent(Component component) {
return childeren.add(component);
}
public boolean removeComponent(Component component) {
return childeren.remove(component);
}
}
데코레이터 패턴 (Decorator Pattern)
데코레이터 패턴이란 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다. 동적으로 책임 추가가 필요할 때 데코레이터 패턴을 사용할 수 있다.
import java.util.Scanner;
public class DecoratorPattern {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
IBeverage beverage = new Base();
boolean done = false;
while (!done) {
System.out.println("음료 현재가격" + beverage.getTotalPrice());
System.out.println("선택 : 1샷 추가 / 2: 우유 추가");
switch (scan.nextInt()) {
case 0:
done = true;
break;
case 1:
beverage = new Espresso(beverage);
break;
case 2:
beverage = new Milk(beverage);
break;
}
}
System.out.println("음료가격 : "+ beverage.getTotalPrice());
scan.close();
}
}
interface IBeverage {
// 총 가격
int getTotalPrice();
}
class Base implements IBeverage {
@Override // 실질적인 책임의 주체
public int getTotalPrice() {
return 0; // 아무것도 없는 상태에서는 가격이 산출되지 않음
}
}
abstract class AbstAding implements IBeverage {
private IBeverage base; // 컴포턴트워 장식을 동시에함
public AbstAding(IBeverage base) {
super();
this.base = base;
}
protected IBeverage getBase() {
return base;
}
@Override
public int getTotalPrice() {
return base.getTotalPrice();
}
}
class Milk extends AbstAding {
public Milk(IBeverage meterial) {
super(meterial);
}
public int getTotalPrice() {
return super.getTotalPrice() + 50;
}
}
class Espresso extends AbstAding {
static protected int espressoCount = 0;
public Espresso(IBeverage base) {
super(base);
}
@Override
public int getTotalPrice() {
return super.getTotalPrice() + getAddPrice();
}
private static int getAddPrice() {
espressoCount += 1;
int addPrice = 100;
if (espressoCount > 1) {
addPrice = 70;
}
return addPrice;
}
}
비지터 패턴 (Visitor Pattern)
객체 지향 프로그래밍과 소프트웨어 공학에서 비지터 패턴은 알고리즘을 객체 구조에서 분리시키는 디자인 패턴이다. 이렇게 분리를 하면 구조를 수정하지 않고도 실질적으로 새로운 동작을 기존의 객체 구조에 추가할 수 있게 된다. 개방-폐쇄 원칙을 적용하는 방법의 하나이다.
- 객체에서 처리를 분리해서 사용할 수 있다.
- 객체class), 처리(method) ,분리
import java.util.ArrayList;
public class VisitorPattern {
public static void main(String[] args) {
VisitorA v1 = new VisitorA();
ArrayList<Visitoable> visitables = new ArrayList<Visitoable>();
visitables.add(new VisitoableA(1));
visitables.add(new VisitoableA(2));
visitables.add(new VisitoableA(3));
visitables.add(new VisitoableA(4));
visitables.add(new VisitoableA(5));
Visitor visitor = new VisitorA();
int ageSum=0;
for (Visitoable visitable : visitables) {
visitable.accept(visitor);
ageSum += ((VisitoableA) visitable).getAge();
}
System.out.println(ageSum);
System.out.println(((VisitorA) visitor).getAgeSum());
}
}
interface Visitor {
public void visit(Visitoable visitoable);
}
interface Visitoable {
public void accept(Visitor visitor);
}
class VisitoableA implements Visitoable {
private int age;
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public VisitoableA(int age) {
super();
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class VisitorA implements Visitor {
private int ageSum;
public VisitorA() {
this.ageSum = 0;
}
@Override
public void visit(Visitoable visitoable) {
if (visitoable instanceof VisitoableA) {
ageSum += ((VisitoableA) visitoable).getAge();
} else {
// ...
}
}
public int getAgeSum() {
return ageSum;
}
}
책임 연쇄 패턴 (Chain-of-responsibility pattern)
객체 지향 디자인에서 chain-of-responsibility pattern은 명령 객체와 일련의 처리 객체를 포함하는 디자인 패턴이다. 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이고, 체인 안의 처리 객체가 핸들할 수 없는 명령은 다음 처리 객체로 넘겨진다.
- 다양한 처리방식을 유연하게 처리할 수 있다
- 처리를 해보고 안되면 다음으로 넘겨주는 방식으로 처리하는 소스코드
public class ChainofResposibility {
public static void main(String[] args) {
Calculator plus = new PlusCalculator();
Calculator sub = new SubCalculator();
plus.setNextCalculator(sub);
Request requests1 = new Request(1,2,"+");
Request requests2 = new Request(10,2,"-");
plus.process(requests1);
plus.process(requests2);
}
}
abstract class Calculator{
private Calculator nextCalculator;
public void setNextCalculator(Calculator nextCalculator) {
this.nextCalculator = nextCalculator;
}
public Calculator getNextCalculator() {
return nextCalculator;
}
public boolean process(Request request){
if(operator(request)){
//더하기 작업
return true;
}else{
if(nextCalculator != null){
return nextCalculator.process(request);
}
return false;
}
}
//동작
abstract protected boolean operator(Request request);
}
class Request{
private int a,b;
private String oprator;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public String getOprator() {
return oprator;
}
public void setOprator(String oprator) {
this.oprator = oprator;
}
public Request(int a, int b, String oprator) {
super();
this.a = a;
this.b = b;
this.oprator = oprator;
}
}
class PlusCalculator extends Calculator{
@Override
protected boolean operator(Request request) {
if(request.getOprator().equals("+")){
int a = request.getA();
int b = request.getB();
System.out.println(a+"+"+b+"="+(a+b));
}
return false;
}
}
class SubCalculator extends Calculator{
@Override
protected boolean operator(Request request) {
if(request.getOprator().equals("-")){
int a = request.getA();
int b = request.getB();
System.out.println(a+"-"+b+" = "+ (a-b));
}
return false;
}
}
확장된 개념
package designpattern;
import java.awt.geom.Area;
public class ChainofResposibility2 {
//게임에서 공격을 주고받는 것을 책임사슬 패턴을 이용해서 이용
// 효율적이지는 않으나 이해하기위해 만듬
public static void main(String[] args) {
Attack attack = new Attack(100);
Amor amor1 = new Amor(10);
Amor amor2 = new Amor(15);
//첫번재 공격
amor1.setNextDefense(amor2);
amor1.defense(attack);
System.out.println(attack.getAmount());
//동적 point2
Defense defense = new Defense() {
@Override
public void defense(Attack attack) {
int amount = attack.getAmount();
attack.setAmount(amount-=50);
}
};
//게임 도중 추가 착용 두번째 공격
amor2.setNextDefense(defense);
attack.setAmount(100);
amor1.defense(attack);
System.out.println(attack.getAmount());
//동적으로 책임사슬을 더 추가하여 사용할 수 있음 !
}
}
class Attack{
private int amount;
public Attack(int amount){
this.amount=amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getAmount() {
return amount;
}
}
interface Defense{
public void defense(Attack attack);
}
class Amor implements Defense{
private Defense nextDefense;
private int def;
public Amor(int def){
this.def = def;
}
public void setNextDefense(Defense nextDefense) {
this.nextDefense = nextDefense;
}
public void setDef(int def) {
this.def = def;
}
@Override
public void defense(Attack attack) {
//중요 처리 부분
proccess(attack); //처리를 안해주고 무조건 호출
if(nextDefense!=null){
nextDefense.defense(attack);
}
}
private void proccess(Attack attack) {
int amount = attack.getAmount();
amount -= def;
attack.setAmount(amount);
}
}
이상으로 [디자인 패턴] Java를 이용한 소스로 보는 디자인 패턴 종류 정리 2편을 마치도록 하겠습니다.