デコレータモードについての興味深い話。一生忘れることはありません。

この記事は「これがデザインパターンの学習方法です」からの抜粋です。

1デコレータパターンを使用して、パンケーキのオーバーコーディングの問題を解決します

そんなシーンを見ると、サラリーマンの多くは夜更かしする癖があり、毎朝仕事が大変なので、朝食の問題を解決するためにもっと便利な方法で寝る人が多いです。人々は朝食にパンケーキを食べるかもしれません。パンケーキに卵やソーセージを加えることもできますが、どのように加えてもパンケーキです。別の例として、ケーキに果物を追加したり、家を飾ったりすることは、すべてデコレータモードです。

コードを使用して、パンケーキにコードを追加するビジネスシナリオをシミュレートしてみましょう。まず、デコレータモードがない状況を見てみましょう。まず、パンケーキバターケーキクラスを作成します。


public class Battercake {

    protected String getMsg(){
        return "煎饼";
    }

    public int getPrice(){
        return 5;
    }

}

复制代码

次に、卵を追加するBattercakeWithEggクラスを作成します。


public class BattercakeWithEgg extends Battercake{
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }

    @Override
    //加1个鸡蛋加1元钱
    public int getPrice() {
        return super.getPrice() + 1;
    }
}

复制代码

卵とソーセージの両方を追加する別のBattercakeWithEggAndSausageクラスを作成します。


public class BattercakeWithEggAndSausage extends BattercakeWithEgg{
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }

    @Override
    //加1根香肠加2元钱
    public int getPrice() {
        return super.getPrice() + 2;
    }
}

复制代码

最後に、クライアントテストコードを記述します。


public static void main(String[] args) {

        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg() + ",总价格:" + battercake.getPrice());

        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + 
			battercakeWithEgg.getPrice());

        Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + 
			battercakeWithEggAndSausage.getPrice());

    }
		
复制代码

実行結果を下図に示します。

file

操作の結果は問題ありません。ただし、ユーザーが卵2個とソーセージ1個のパンケーキを必要とする場合、現在のクラス構造で作成することはできません。また、カスタマイズ用に別のクラスを作成しない限り、価格を自動的に計算することもできません。需要が再び変化した場合、カスタマイズを追加し続けることは明らかに非科学的です。デコレータパターンは、上記の問題を解決するために以下で使用されます。まず、パンケーキ用の抽象的なBattercakeクラスを作成します。


public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}

复制代码

基本的なパンケーキ(またはベースミール)BaseBattercakeを作成します。


public class BaseBattercake extends Battercake {
    protected String getMsg(){
        return "煎饼";
    }

    public int getPrice(){ return 5;  }
}

复制代码

次に、パッケージを拡張する抽象デコレータBattercakeDecoratorクラスを作成します。


public abstract class BattercakeDecorator extends Battercake {
    //静态代理,委派
    private Battercake battercake;

    public BattercakeDecorator(Battercake battercake) {
        this.battercake = battercake;
    }
    protected abstract void doSomething();

    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }
    @Override
    protected int getPrice() {
        return this.battercake.getPrice();
    }
}

复制代码

次に、卵デコレータEggDecoratorクラスを作成します。


public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }

    protected void doSomething() {}

    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }

    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}

复制代码

SausageDecoratorクラスを作成します。


public class SausageDecorator extends BattercakeDecorator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }

    protected void doSomething() {}

    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }
    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}

复制代码

次に、クライアントテストコードを記述します。


public class BattercakeTest {
    public static void main(String[] args) {
        Battercake battercake;
        //买一个煎饼
        battercake = new BaseBattercake();
        //煎饼有点小,想再加1个鸡蛋
        battercake = new EggDecorator(battercake);
        //再加1个鸡蛋
        battercake = new EggDecorator(battercake);
        //很饿,再加1根香肠
        battercake = new SausageDecorator(battercake);

        //与静态代理的最大区别就是职责不同
        //静态代理不一定要满足is-a的关系
        //静态代理会做功能增强,同一个职责变得不一样

        //装饰器更多考虑的是扩展
        System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
    }
}

复制代码

実行結果を下図に示します。

file

最後に、次の図に示すように、クラス図を見てください。

file

2デコレータパターンでログ形式の出力を拡張します

印象を深めるために、別のアプリケーションシナリオを見てみましょう。要件はおおまかに次のとおりです。システムはSLSサービスを使用してプロジェクトログを監視します。プロジェクトログはJSON形式で解析されるため、プロジェクトのログをJSON形式にカプセル化してから印刷する必要があります。既存のログシステムは、Log4j+Slf4jフレームワークを使用して構築されています。クライアントコールは次のとおりです。


  private static final Logger logger = LoggerFactory.getLogger(Component.class);
        logger.error(string);
				
复制代码

这样打印出来的是毫无规则的一行行字符串。当考虑将其转换成JSON格式时,笔者采用装饰器模式。目前有的是统一接口Logger和其具体实现类,笔者要加的就是一个装饰类和真正封装成JSON格式的装饰产品类。创建装饰器类DecoratorLogger。


public class DecoratorLogger implements Logger {

    public Logger logger;

    public DecoratorLogger(Logger logger) {

        this.logger = logger;
    }

    public void error(String str) {}

    public void error(String s, Object o) {

    }
    //省略其他默认实现
}

复制代码

创建具体组件JsonLogger类。


public class JsonLogger extends DecoratorLogger {
    public JsonLogger(Logger logger) {
        super(logger);
    }
        
    @Override
    public void info(String msg) {

        JSONObject result = composeBasicJsonResult();
        result.put("MESSAGE", msg);
        logger.info(result.toString());
    }
    
    @Override
    public void error(String msg) {
        
        JSONObject result = composeBasicJsonResult();
        result.put("MESSAGE", msg);
        logger.error(result.toString());
    }
    
    public void error(Exception e) {

        JSONObject result = composeBasicJsonResult();
        result.put("EXCEPTION", e.getClass().getName());
        String exceptionStackTrace = Arrays.toString(e.getStackTrace());
        result.put("STACKTRACE", exceptionStackTrace);
        logger.error(result.toString());
    }
    
    private JSONObject composeBasicJsonResult() {
        //拼装了一些运行时的信息
        return new JSONObject();
    }
}

复制代码

可以看到,在JsonLogger中,对于Logger的各种接口,我们都用JsonObject对象进行一层封装。在打印的时候,最终还是调用原生接口logger.error(string),只是这个String参数已经被装饰过了。如果有额外的需求,则可以再写一个函数去实现。比如error(Exception e),只传入一个异常对象,这样在调用时就非常方便。 另外,为了在新老交替的过程中尽量不改变太多代码和使用方式,笔者又在JsonLogger中加入了一个内部的工厂类JsonLoggerFactory(这个类转移到DecoratorLogger中可能更好一些)。它包含一个静态方法,用于提供对应的JsonLogger实例。最终在新的日志体系中,使用方式如下。


    private static final Logger logger = JsonLoggerFactory.getLogger(Client.class);

    public static void main(String[] args) {

        logger.error("错误信息");
    }
		
复制代码

对于客户端而言,唯一与原先不同的地方就是将LoggerFactory改为JsonLoggerFactory即可,这样的实现,也会更快更方便地被其他开发者接受和习惯。最后看如下图所示的类图。

file

装饰器模式最本质的特征是将原有类的附加功能抽离出来,简化原有类的逻辑。通过这样两个案例,我们可以总结出来,其实抽象的装饰器是可有可无的,具体可以根据业务模型来选择。

关注『 Tom弹架构 』回复“设计模式”可获取完整源码。

【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注『 Tom弹架构 』可获取更多技术干货!

おすすめ

転載: juejin.im/post/7025517932885049357