본문 바로가기
정보처리기사/필기

[정보처리기사 필기 요약] 디자인 패턴(2) - 구조패턴

by 채연2 2021. 3. 2.

* 구조패턴(Structural Pattern) : 다른 기능을 가진 객체 간 협력 필요할 때 객체들을 조직화 하는 방법과 기능 구현하기 위해 객체 구성하는 방식 제공

 

종류 개념
Adaptor - 클래스 재사용성 높이기 위해 클래스 간 기능 변환 제공하여 호환성 확보
Bridge - 인터페이스(API)가 서로 다른 클래스 연결하는 패턴. 기능의 계층과 구현의 계층 연결
Composite - 복잡한 객체 구조 표현하여 객체 집합 속에 또 다른 객체 집합 가짐
Decorator - 새로운 기능 추가될 때마다 새로운 객체 내부에서도 그대로 유지, 보장해줌
Facade - 서브 시스템 복잡할 경우 간단한 인터페이스 통해 서브시스템 주요 기능 사용 가능
Fly weight - 인스턴스 가능한 한 공유시켜 불필요한 생성 하지 않도록 함
Proxy - 객체 접근 제어하려는 목적으로 인터페이스 역할 하는 객체 사용하여 제어

 

 

* Adaptor Pattern 예

- 어떤 인터페이스를 클라이언트에서 요구하는 형태의 인터페이스에 적응시켜주는 역할

public interface Duck {
	public void quack();
	public void fly();
}

public class MallardDuck implements Duck {
	@Override
	public void quack() {
		System.out.println("Quack");
	}

	@Override
	public void fly() {
		System.out.println("I'm flying");
	}
}

public interface Turkey {
	public void gobble();
	public void fly();
}

public class WildTurkey implements Turkey{
	@Override
	public void gobble() {
		System.out.println("Gobble gobble");
	}
         
	@Override
	public void fly() {
		System.out.println("I'm flying a short distance")
	}
}

//Duck 객체 대신 Turkey 객체 사용해야 하는 상황이라면 ? 어댑터 생성
public class TurkeyAdapter implements Duck {
	Turkey turkey;

	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}

	@Override
	public void quack(){ 
		turkey.gobble();
	}

	@Override
	public void fly() {
		turkey.fly();
	}
}

public class DuckTestDrive {
	public static void main(String[] args) {
		MallardDuck duck = new MallardDuck();
		WildTurkey turkey = new WildTurkey();
		Duck turkeyAdapter = new TurkeyAdapter(turkey);

		System.out.println("The turkey says...");
		turkey.gobble();
		turkey.fly();

		System.out.println("The Duck says...");
		testDuck(duck);

		System.out.println("The TurkeyAdapter says...");
		testDuck(turkeyAdapter);
	}

	public static void testDuck(Duck duck){ 
		duck.quack();
		duck.fly();
	}
}

 

 

* Bridge Pattern 예

▶Shape 구현체인 Triangle, Pentagon에서 다시 Color 라는 추상층 존재. 위를 Bridge Pattern 사용하여 인터페이스 분리

public interface Color {
	public void applyColor();
}

public abstract class Shape {
	//Composition - implementor
	protected Color color;
	
	public Shape(Color c){
		this.color=c;
	}
	
	abstract public void applyColor();
}

public class Triangle extends Shape{
	public Triangle(Color c) {
		super(c);
	}

	@Override
	public void applyColor() {
		System.out.print("Triangle filled with color ");
		color.applyColor();
	} 
}

public class Pentagon extends Shape{
	public Pentagon(Color c) {
		super(c);
	}

	@Override
	public void applyColor() {
		System.out.print("Pentagon filled with color ");
		color.applyColor();
	} 
}

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

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

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();
	}
}

 

 

* Composite Pattern 예

- 객체들 관계 트리 구조로 구성하여 부분-전체 계층 표현. 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 함

/** "Component" */
interface Graphic {
	public void print();
}

/** "Composite" */
class CompositeGraphic implements Graphic {
    private List<Graphic> mChildGraphics = new ArrayList<Graphic>();

    public void print() {
        for (Graphic graphic : mChildGraphics) {
            graphic.print();
        }
    }

    public void add(Graphic graphic) {
        mChildGraphics.add(graphic);
    }

    public void remove(Graphic graphic) {
        mChildGraphics.remove(graphic);
    }
}

/** "Leaf" */
class Ellipse implements Graphic {
    public void print() {
        System.out.println("Ellipse");
    }
}

public class Program {
    public static void main(String[] args) {
        Ellipse ellipse1 = new Ellipse();
        Ellipse ellipse2 = new Ellipse();
        Ellipse ellipse3 = new Ellipse();
        Ellipse ellipse4 = new Ellipse();

        CompositeGraphic graphic = new CompositeGraphic();
        CompositeGraphic graphic1 = new CompositeGraphic();
        CompositeGraphic graphic2 = new CompositeGraphic();

        graphic1.add(ellipse1);
        graphic1.add(ellipse2);
        graphic1.add(ellipse3);

        graphic2.add(ellipse4);

        graphic.add(graphic1);
        graphic.add(graphic2);

        //Prints the complete graphic (four times the string "Ellipse").
        graphic.print();
    }
}

 

 

* Decorator Pattern 예

- 원본에 무언가를 더  입혀 새로운 것을 만드는 것

- 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공

public class Cake {
    public String getCake() {
        return "cake";
    }
}

public class CreamDecorator extends Cake{
    private Cake cake;
     
    public CreamDecorator(Cake cake) {
        this.cake = cake;
    }
     
    @Override
    public String getCake() {
        return "*cream* "+cake.getCake() + " *cream*";
         
    }
}

public class StrawberryDecorator extends Cake{
    private Cake cake;
     
    public StrawberryDecorator(Cake cake) {
        this.cake = cake;
    }
     
    @Override
    public String getCake() {
        return "@strawberry@ "+cake.getCake() + " @strawberry@";
    }
}

public class Client {
    public static void main(String[] args) {
        Cake cake = new Cake();
        Cake creamCake = new CreamDecorator(cake);
        Cake strawberryCreamCake = new StrawberryDecorator(creamCake);
        System.out.println(strawberryCreamCake.getCake());
    }
}

//result 
@strawberry@ *cream* cake *cream* @strawberry@

 

 

* Facade Pattern 예

- 클래스 라이브러리 같은 어떤 소프트웨어의 다른 커다란 코드 부분에 대한 간략화된 인터페이스를 제공

//Facade Pattern 미적용
//영화 시청 : 팝콘 준비 -> TV 켜기 -> 영화 검색 -> 영화 결제 -> 영화 재생
Snack snack = new Snack("popcon);
RemoteControl remote= new RemoteControl();
Movie movie = new Movie("joker");

snack.Prepare();
remote.turnOn();
movie.searchMovie();
movie.chargeMovie();
movie.playMovie();
//Facade Pattern 적용
//영화를 보기 위해 사용하는 서브클래스들 사이의 간단한 통합 인터페이스를 제공하는 역할
//파사드 패턴을 사용하면 위의 클라이언트 main안의 viewMovie() 함수 하나만 호출하면
//간식 준비, TV 켜기, 영화 검색, 영화 구입, 영화보기를 모두 진행 가능

//복잡한 소프트웨어 바깥쪽의 코드가 라이브러리의 안쪽 코드에 의존하는 일을 감소시켜 주고,
//복잡한 소프트웨어를 사용할 수 있게 간단한 인터페이스를 제공
public class RemoteControl {
    public void turnOn() {
        System.out.println("TV를 켜다");
    }
    
    public void turnOff() {
        System.out.println("TV를 끄다");
    } 
}

public class Movie {
    private String name="";
    
    public Movie(String name) {
        this.name = name;
    }
    
    public void searchMovie() {
        System.out.println(name+" 영화를 찾다");
    }
    
    public void chargeMovie() {
        System.out.println("영화를 결제하다");
    }
    
    public void playMovie() {
        System.out.println("영화 재생");
    } 
}

public class Snack {
    private String name="";
    
    public Snack(String name) {
        this.name = name;
    }
    
    public void Prepare() {
        System.out.println(name+" 간식 준비 완료 ");
    }
 
}

public class Facade {
    private String snackName ="";
    private String movieName="";
    
    public Facade(String snackName,String movieName) {
        this.snackName=snackName;
        this.movieName=movieName;
    }
    
    public void viewMovie() {
        Snack snack = new Snack(snackName);
        RemoteControl remote= new RemoteControl();
        Movie movie = new Movie(movieName);

        snack.Prepare();
        remote.turnOn();
        movie.searchMovie();
        movie.chargeMovie();
        movie.playMovie();
    }
}

public class Client {
    public static void main(String[] args) {
        Facade facade= new Facade("popcon","joker");
        facade.viewMovie();
    }
}

 

 

* Fly weight Pattern 예

- 비용이 큰 자원을 공통으로 사용할 수 있도록 만드는 패턴

> 비용이 큰 경우 : 중복 생성 가능성 높은 경우, 자원 생성 비용은 크지만 사용 빈도 낮은 경우

public class Car {
    private String name;

    public Car(String data) {
        this.name = data;
    }

    public String getName() {
        return name;
    }
}

public class CarFactory {
    Map pool;

    public CarFactory() {
        pool = new HashMap();
    }

    public Car getCar(String key) {
        Car car = (Car) pool.get(key);
        if (car == null) {
            car = new Car(key);
            pool.put(key, car);
            System.out.println("새로 생성 : " + key);
        } else {
            System.out.println("재사용 : " + key);
        }
        return car;
    }
}

public class Client {
    public static void main(String[] args) {
        CarFactory factory = new CarFactory();

        System.out.println(factory.getCar("Car A"));
        System.out.println(factory.getCar("Car B"));
        System.out.println(factory.getCar("Car C"));
        System.out.println(factory.getCar("Car B"));
        System.out.println(factory.getCar("Car A"));
        System.out.println(factory.getCar("Car A"));
    }
}

//result
새로 생성 : Car A
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@6e0be858
새로 생성 : Car B
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@61bbe9ba
새로 생성 : Car C
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@610455d6
재사용 : Car B
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@61bbe9ba
재사용 : Car A
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@6e0be858
재사용 : Car A
k.bs.designpatternsp.pattern.flyweight.ja.car.Car@6e0be858

 

 

* Proxy Pattern 예

- 실제 기능을 수행하는 객체 Real Object 대신 가상의 객체 Proxy Object를 사용해 로직의 흐름을 제어.

 즉, 제어 흐름을 조정하기 위한 목적으로 중간에 대리자를 두는 패턴

1. 대리자는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용

2. 대리자는 실제 서비스에 대한 참조 변수를 가짐(함성).

3. 대리자는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려줌

4. 대리자는 실제 서비스의 메서드 호출 전후에 별도의 로직을 수행 가능

//Proxy Pattern 미사용
class Service {
    public String greeting(String str) {
        return "안녕하세요 " + str + "입니다.";
    }
}

public class Client {
    public static void main(String[] args) {
        Service service = new Service();
        System.out.println(service.greeting("bsjo"));
    }
}

//Proxy Pattern 사용
interface IService {
    public String greeting(String str);
}

class Service implements IService{
    @Override
    public String greeting(String str) {
        return "안녕하세요 " + str + "입니다.";
    }
}

class Proxy implements IService {
    IService iService;
    
    @Override
    public String greeting(String str) {
        System.out.println("호출에 대한 흐름 제어가 주목적이며, 반환 결과를 그대로 전달한다.");
        iService = new Service();
        return iService.greeting(str);
    }
}

public class Client {
    public static void main(String[] args) {
        IService service = new Proxy();
        System.out.println(service.greeting("kkkkk"));
    }
}

 

 

참고 : beomseok95.tistory.com/category/DesignPattern

320x100

댓글