Interesante charla sobre el modo decorador, para que no lo olvides de por vida

Este artículo es un extracto de "Así es como se deben aprender los patrones de diseño"

1 Use el patrón decorador para resolver el problema de sobrecodificación de panqueques

Mirando tal escena, la mayoría de los trabajadores de oficina tienen la costumbre de dormir hasta tarde y están muy apretados en el trabajo todas las mañanas. Por lo tanto, muchas personas usan una forma más conveniente de resolver el problema del desayuno para dormir más. Algunos la gente puede comer panqueques para el desayuno. Puede agregar huevos o salchichas al panqueque, pero no importa cómo lo agregue, sigue siendo un panqueque. Para otro ejemplo, agregar un poco de fruta a un pastel y decorar una casa son todos modos de decorador.

Usemos el código para simular el escenario comercial de agregar código a los panqueques. Primero veamos la situación sin el modo decorador. Primero cree una clase Battercake de panqueques.


public class Battercake {

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

    public int getPrice(){
        return 5;
    }

}

复制代码

Luego cree una clase BattercakeWithEgg que agregue huevos.


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

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

复制代码

Cree otra clase BattercakeWithEggAndSausage que agregue huevos y salchichas.


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

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

复制代码

Finalmente escriba el código de prueba del cliente.


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

    }
		
复制代码

El resultado de la ejecución se muestra en la siguiente figura.

file

El resultado de la operación no es problema. Sin embargo, si el usuario necesita un panqueque con 2 huevos y 1 salchicha, no se puede crear con la estructura de clases actual, ni se puede calcular el precio automáticamente a menos que se cree otra clase para personalizar. Si la demanda cambia nuevamente, obviamente no es científico seguir agregando personalización. El patrón decorador se usa a continuación para resolver el problema anterior. Primero cree una clase abstracta Battercake para panqueques.


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

复制代码

Crea un panqueque básico (o comida base) BaseBattercake.


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

    public int getPrice(){ return 5;  }
}

复制代码

A continuación, cree una clase de decorador abstracto BattercakeDecorator que amplíe el paquete.


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

复制代码

A continuación, cree la clase EggDecorator del decorador de huevos.


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

复制代码

Cree la clase 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;
    }
}

复制代码

Luego escriba el código de prueba del cliente.


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

复制代码

El resultado de la ejecución se muestra en la siguiente figura.

file

Finalmente, mire el diagrama de clases, como se muestra en la siguiente figura.

file

2 Extienda la salida del formato de registro con el patrón decorador

Para profundizar la impresión, veamos otro escenario de aplicación. Los requisitos son más o menos los siguientes: el sistema usa el servicio SLS para monitorear el registro del proyecto, que se analiza en formato JSON. Por lo tanto, es necesario encapsular el registro en el proyecto en formato JSON y luego imprimirlo. El sistema de registro existente se construye utilizando el marco Log4j + Slf4j. La llamada del cliente es la siguiente.


  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弹架构 』可获取更多技术干货!

Supongo que te gusta

Origin juejin.im/post/7025517932885049357
Recomendado
Clasificación