'Design Pattern/Structural Patterns(구조적 패턴)'에 해당되는 글 1건

- @참고 : https://refactoring.guru/design-patterns/facade

 

Facade

Intent Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes. Problem Imagine that you must make your code work with a broad set of objects that belong to a sophisticated l

refactoring.guru

🙌 취지

퍼사드는 라이브러리, 프레임워크, 또는 기타 복잡한 클래스 집합에 심플한 인터페이스를 제공하는 구조적 디자인 패턴입니다.

🤔 문제점

복잡한 라이브러리나 프레임워크에 속한 광범위한 객체 집합에 대해 코드를 작동시켜야 한다고 상상해보십시오. 일반적으로, 당신은 모든 객체를 초기화해야하고, 의존성을 추적하고, 올바른 순서로 메소드를 실행하는 등등을 합니다.

그 결과, 당신의 클래스에 대한 비즈니스 로직은 타사 클래스의 구현 세부사항과 밀접하게 결합되고, 이는 이해와 유지를 어렵게 합니다.

😊 해결책

퍼사드는 많은 동적 부분을 포함하는 복잡한 서브시스템에 단순한 인터페이스를 제공하는 클래스입니다. 퍼사드는 서브시스템을 직접 사용하는 것과 비교하여 제한된 기능을 제공할 수 있습니다. 그러나 클라이언트가 실제로 관심을 갖는 기능만 포함합니다.

수십가지 기능을 갖는 복잡한 라이브러리와 앱을 통합해야 할 필요가 있을 때 퍼사드를 사용하는 것이 편리하지만, 약간의 기능이 필요합니다.

예를 들어, 고양이에 대한 짧은 재밌는 동영상을 소셜 미디어에 업로드하는 앱은 전문적인 비디오 변환 라이브러리를 사용할 수 있습니다. 그러나 필요한 모든 것은 encode(filename, format)이라는 단일 메소드를 가지는 클래스입니다. 이러한 클래스를 생성하고 동영상 변환 라이브러리와 연결하면, 첫번째 퍼사드가 됩니다.

🚗 실세계에 비유

전화로 주문하기

상점에 전화를 걸어 전화 주문을 하면, 운영자는 상점의 모든 서비스와 부서에 대한 퍼사드(정면)입니다. 운영자는 주문 시스템, 지불 게이트웨이 및 다양한 배송 서비스에 대한 간단한 음성 인터페이스를 제공합니다.

🔦구조

1. Facade는 서브시스템 기능의 특정 부분에 편리하게 접근할 수 있도록 합니다. 고객의 요청을 지시하는 위치와 모든 동적 부품을 작동하는 방법을 압니다.

2. 추가적 퍼사드(Additional Facade) 클래스는 또다른 복잡한 구조를 만들 수 있는 관련 없는 기능으로 단일 퍼사드를 오염하는 걸 방지하기 위해 생성될 수 있습니다. 추가 퍼사드는 클라이언트와 다른 퍼사드 모두에게 사용될 수 있습니다.

3. 복잡한 서브시스템(Complex Subsystem)은 수십의 다양한 객체로 구성됩니다. 이들 모두가 의미있는 일을 하게 하려면, 올바른 순서로 객체를 초기화하고 데이터를 적절한 포맷으로 제공하는 것과 같은 서브시스템의 구현 세부사항을 자세히 살펴봐야 합니다.

 서브시스템 클래스는 퍼사드의 존재를 인식하고 있지 않습니다. 이들은 시스템 내에서 작동하고 서로 직접 작업합니다.

# 짜가코드

이 예제에서, 퍼사드 패턴은 복잡한 비디오 변환 프레임워크와의 상호작용을 단순화합니다.

단일 퍼사드 클래스 내에서 여러 종속성을 격리하는 예제

코드를 수십의 프레임워크 클래스와 직접 작동시키는 대신, 해당 기능을 캡슐화하고 코드의 나버지 부분에서 숨기는 퍼사드 클래스를 생성합니다. 이 구조는 또한 향후 버전의 프레임워크로 업그레이드 하거나 다른 것과 대체하는 노력을 최소화하는걸 돕습니다. 당신이 앱에서 바꿀 필요가 있는 것은 퍼사드 메소드의 구현뿐입니다.

//이것은 복잡한 타사 비디오 변환 프레임워크 클래스의 일부입니다. 코드를 제어하지 않으므로 단순화할 수 없습니다.

class VideoFile
// ...

class OggCompressionCodec
// ...

class MPEG4CompressionCodec
// ...

class CodecFactory
// ...

class BitrateReader
// ...

class AudioMixer
// ...


//프레임워크의 복잡성을 단순한 인터페이스 뒤에 숨기기 위해 퍼사드 클래스를 만듭니다. 기능과 단순성 간의 균형을 유지합니다.
class VideoConverter is
	method convert(filename, format):File is
    	file = new VideoFile(filename)
        sourceCodec = new CodeFactory.extract(file)
        if (format == "mp4")
        	destinationCodec = new MPEG4CompressionCodec()
        else
        	destinationCodec = new OggCompressionCodec()
		buffer = BitrateReader.read(filename, sourceCodec)
        result = BitrateReader.convert(buffer, destinationCodec)
        result = (new AudioMixer()).fix(result)
        return new File(result)
        
//어플리케이션 클래스는 복잡한 프레임워크에서 제공하는 10억개의 클래스에 의존하지 않습니다. 또한, 프레임워크를 전환하기로 결정한다면, 퍼사드 클래스만 다시 작성하면 됩니다.
class Application is
	method main() is
    	converter = new VideoConverter()
        mp4 = convertor.convert("funny-cats-video.ogg", "mp4")
        mp4.save()

💡 적용

  • 복잡한 서브시스템에 대한 제한적이지만 간단한 인터페이스가 필요한 경우 퍼사드 패턴을 사용합니다.
    • 종종, 서브시스템은 시간이 지남에 따라 더욱 복잡해집니다. 디자인 패턴을 적용하더라도 일반적으로 더 많은 클래스를 생성하게 합니다. 서브시스템은 다양한 상황에서 더 유연하고 재사용하기 쉬워질 수 있지만, 클라이언트에서 요구하는 구성과 상용구 코드의 양은 점점 더 커지고 있습니다. 퍼사드는 클라이언트의 요구사항에 맞는 서브시스템의 가장 많이 사용되는 기능에 대한 바로가기를 제공하여 이러한 문제를 해결하려 합니다.
  • 서브시스템을 계층으로 구조화하려는 경우 퍼사드를 사용합니다.
    • 서브시스템의 각 단계에 대한 진입점을 정의하는 퍼사드를 작성하세요. 퍼사드를 통해서만 통신하게 함으로써 다중 서브시스템 간의 커플링을 줄일 수 있습니다.
    • 예를 들어, 동영상 변환 프레임워크로 돌아가 보겠습니다. 비디오와 오디오 관련의 두 계층으로 나눌 수 있습니다. 각 계층에 대해, 퍼사드를 생성하고 각 계층의 클레스가 해당 퍼사드를 통해 서로 통신하도록 할 수 있습니다. 이 방법은 중재자 패턴과 매우 유사합니다.

📋 구현 방법

  1. 기존 서브시스템이 제공하는 것보다 더 간단한 인터페이스를 제공할 수 있는지 체크합니다. 인터페이스가 클라이언트 코드를 많은 서브시스템의 클래스와 독립적으로 만들고 있으면 올바른 길을 가고 있습니다.
  2. 새 퍼사드 클래스에서 이 인터페이스를 선언하고 구현합니다. 퍼사드는 호출을 클라이언트 코드로부터 서브시스템의 적절한 객체로 리다이렉트해야 합니다. 퍼사드는 클라이언트 코드가 이미 수행하지 않는 한 서브시스템을 초기화하고 추가 수명 주기를 관리해야 합니다.
  3. 패텀의 이점을 최대한 활용하려면 모든 클라이언트 코드가 퍼사드를 통해서만 서브시스템과 통신하도록 하세요. 그러면 클라이언트 코드는 서브시스템 코드의 변경 사항으로 부터 보호됩니다. 예를 들어, 서브시스템이 새 버전으로 업그레이드 되면 퍼사드의 코드만 수정하면 됩니다.
  4. 퍼사드가 너무 커지면, 동작 부분을 새롭고 세련된 퍼사드 클래스로 추출하는 걸 고려합니다.

⚖ 장단점

✔ 서브시스템의 복잡성으로 부터 코드를 분리할 수 있습니다.

❌ 퍼사드는 앱의 모든 클래스와 연결된 신 객체가 될 수 있습니다.

🐱‍🏍 다른 패턴과의 관계

  • 퍼사드는 기존 객체에 대한 새 인터페이스를 정의하는 반면, 어댑터는 기존 인터페이스를 사용 가능하게 만듭니다. 어댑터는 보통 한 객체를 감싸는 반면, 퍼사드는 객체의 전체 서브시스템에서 작동합니다.
  • 추상 팩토리는 클라이언트 코드에서 서브시스템의 객체가 생성되는 방식만 숨기려는 경우 퍼사드의 대체로 쓸 수 있습니다.
  • Flyweight는 많은 작은 객체를 만드는 방법을 보여주는 반면, 퍼사드는 전체 서브시스템을 나타내는 단일 객체를 만드는 방법을 보여줍니다.
  • 퍼사드중재자는 비슷한 작업을 수행합니다. 강하게 결합된 클래스들 간의 콜라보레이션을 조직하려 합니다.
    • 퍼사드는 단순화된 인터페이스를 정의하지만, 새로운 기능을 도입하지는 않습니다. 서브시스템 자체는 퍼사드를 인식하지 못합니다. 서브시스템 내 객체는 직접 통신할 수 있습니다.
    • 중재자는 시스템 구성 요소 간의 통신을 중앙화합니다. 구성 요소는 중개자 객체에 대해서만 알고 있으며 직접 통신하지 않습니다.
  • 퍼사드 클래스는 대부분의 경우 단일 파사드 오브젝트로 충분하기 때문에 종종 싱글톤으로 변환될 수 있습니다.
  • 퍼사드는 복잡한 객체를 버퍼링하고 자체적으로 초기화한다는 점에서 프록시와 유사합니다. 퍼사드와 달리, 프록시는 서비스 객체와 동일한 인터페이스를 가지고 있어 상호 교환이 가능합니다.

</> 코드 예제

복잡한 동영상 변환 라이브러리에 대한 간단한 인터페이스

이 예제에서, 퍼사드는 복잡한 동영상 변환 프레임워크와의 통신을 단순화 합니다.

퍼사드는 프레임워크의 올바른 클래스를 구성하고 올바른 형식으로 결과를 검색하는 모든 복잡성을 처리하는 단일 메소드를 단일 클래스에 제공합니다.

📁 some_complex_media_library: Complex video conversion library

📄 some_complex_media_library/VideoFile.java

package some_complex_media_library;

public class VideoFile {
	private String name;
	private String codecType;
	
	public VideoFile(String name) {
		this.name = name;
		this.codecType = name.substring(name.indexOf(".") + 1);
	}
	
	public String getCodecType() {
		return codecType;
	}
	
	public String getName() {
		return name;
	}
}

📄 some_complex_media_library/Codec.java

package some_complex_media_library;

public interface Codec {
}

📄 some_complex_media_library/MPEG4CompressionCodec.java

package some_complex_media_library;

public class MPEG4CompressionCodec implements Codec {
	public String type = "mp4";
}

📄 some_complex_media_library/OggCompressionCodec.java

package some_complex_media_library;

public class OggCompressionCodec implements Codec {
	public String type = "ogg";
}

📄 some_complex_media_library/CodecFactory.java

package some_complex_media_library;

public class CodecFactory {
	public static Codec extract(VideoFile file) {
		String type = file.getCodecType();
		if (type.equals("mp4")) {
			System.out.println("CodecFactory: extracting mpeg audio...");
			return new MPEG4CompressionCodec();
		}
		else {
			System.out.println("CodecFactory: extracting ogg audio...");
			return new OggCompressionCodec();
		}
	}
}

📄 some_complex_media_library/BitrateReader.java

package some_complex_media_library;

public class BitrateReader {
	public static VideoFile read(VideoFile file, Codec codec) {
		System.out.println("BitrateReader: reading file...");
		return file;
	}
	
	public static VideoFile convert(VideoFile buffer, Codec codec) {
		System.out.println("BitrateReader: writing file...");
		return buffer;
	}
}

📄 some_complex_media_library/AudioMixer.java

package some_complex_media_library;

import java.io.File;

public class AudioMixer {
	public File fix(VideoFile result) {
		System.out.println("AudioMixer: fixing audio...");
		return new File("tmp");
	}
}

📁 some_complex_media_library: Complex video conversion library

📄 some_complex_media_library/VideoFile.java: Facade provides simple interface of video conversion

package facade;

import java.io.File;

import some_complex_media_library.AudioMixer;
import some_complex_media_library.BitrateReader;
import some_complex_media_library.Codec;
import some_complex_media_library.CodecFactory;
import some_complex_media_library.MPEG4CompressionCodec;
import some_complex_media_library.OggCompressionCodec;
import some_complex_media_library.VideoFile;

public class VideoConversionFacade {
	public File convertVideo(String fileName, String format) {
		System.out.println("VideoconversionFacade: conversion started.");
		VideoFile file = new VideoFile(fileName);
		Codec sourceCodec = CodecFactory.extract(file);
		Codec destinationCodec;
		if (format.contentEquals("mp4")) {
			destinationCodec = new MPEG4CompressionCodec();
		} else {
			destinationCodec = new OggCompressionCodec();
		}
		VideoFile buffer = BitrateReader.read(file, sourceCodec);
		VideoFile intermediateResult = BitrateReader.convert(buffer, destinationCodec);
		File result = (new AudioMixer()).fix(intermediateResult);
		System.out.println("VideoConversionFacade: conversion completed.");
		return result;
	}
}

📄 some_complex_media_library/Codec.java

import java.io.File;

import facade.VideoConversionFacade;

public class Demo {
	public static void main(String[] args) {
		VideoConversionFacade converter = new VideoConversionFacade();
		File mp4Video = converter.convertVideo("youtubevideo.ogg", "mp4");
		// ...
	}
}

📄 OutputDemo.txt: Execution result

VideoconversionFacade: conversion started.
CodecFactory: extracting ogg audio...
BitrateReader: reading file...
BitrateReader: writing file...
AudioMixer: fixing audio...
VideoConversionFacade: conversion completed.
블로그 이미지

uchacha

개발자 일지

,