Java23中设计模式之”装饰者模式”


  目录:

一 “开放-闭合原则”(开闭原则)

二   什么是“装饰者模式”--图解

三   “装饰者模式”实例--星笆兹饮料厂

四  没有完美的设计模式--“装饰模式的不足之处”

 

 

一“开放-闭合原则”(开闭原则)

开闭原则: 类应该对拓展开放, 对修改关闭. 听起来好像不太舒服, 类既然需要”拓展’,怎么会不需要修改呢?  开闭原则针对类;  不修改类, 但是需要拓展功能. 使用继承的方式通常是做不到的, 原因是: 继承在编译时决定”行为”, 不是在运行时动态修改行为,也就是所谓的”拓展”,继承的原因是”行为复用”, 不足之处是无法动态拓行为(缺少弹性).

 <head first 设计模式>:

我们的目的是允许类容易拓展, 在不修改代码的情况下, 就可以匹配新的行为, 这样的设计具有弹性可以应对变化,可以接受新的功能应对改变的需求

通常实现开闭原则的方式不止一种, 装饰者模式是很好的例子!  我自己认为: 实现开闭原则要注意以下00(面向对象)原则

  (1)面向接口(接口或者抽象类)编程,

  (2)多用组合, 少用继承

 

举个例子:

   

//使用Person的客户

public class ObserverDesign{

    Person person;

    public ObserverDesign() {

      

}

   public void setPerson(Personp){

   this.person=p;

   }

   public void sayOK(){

   person.say();

   }

}

  //Person接口

  interface Person{

 void say();

 }

  

  //Student类

  class Student implements Person {

@Override

public void say() {

      System.out.println("I am a student");

}

  }

  //Teacher0000000000000000类

  class Teacher implements Person {

@Override

public void say() {

      System.out.println("I am a Teacher");

}

  }

 

ObserverDesign类符合”开闭原则”, ObserverDesign从来不关心Person是哪个具体实例,只关心执行sayOK行为,我们测试一下

 

public class Design{

public static void main(String[] args) {

        //创建ObserverDesign

ObserverDesign obj=new ObserverDesign();

//student

Student stu=new Student();

//teacher

Teacher teacher=new Teacher();

//动态设置属性

obj.setPerson(stu);

obj.sayOK();

//修改

obj.setPerson(teacher);

obj.sayOK();

}

}

 

 

 什么是“装饰者模式”--图解

1)装饰者模式定义: 动态地将责任附加到对象上,若要拓展功能,装饰者提供比继承更有弹性的替代方案

2)来自<head first 设计模式>的一张实现类图(自己不想画图)




   其中Decorate是装饰者抽象接口, ConcreteDecoratorA、ConcreteDecoratorB是

”装饰者”, ConcreteComponent是”被装饰者”

 

“装饰者模式”实例--星笆兹饮料厂

   这个案例来自<head first 设计模式>, 购买饮料时, 我们可以要求在其中加各种不同的调料(变化), 比如买一杯鲜果奶茶, 可以要求在里面加苹果汁、哈密瓜汁、奶酪等调料。

我们还需要计算出饮料+调料的总价钱(没学习设计模式之前你会怎么做? 看看你的程序有没足够的弹性, 又有多少依赖)

 

下面我们使用”装饰者模式”定义的类图来编码:

  首先我们需要一个饮料抽象类, 装饰者和被装饰者都要直接或者间接实现该接口, 想想我们Java中IO流学习时, 我们是不是经常使用BufferInputStream"替换"InputStream? 因为他们实现了统一的接口(类型一致). 装饰者对象替换被装饰对象inputStream,并且拓展了他的功能。所以我们需要实现统一接口。 代码如下:

  饮料抽象接口

public abstract class Beverage{

protected String desc="unkonw";

  //实现

public String getDescription(){

return desc;

}

//抽象,计算价格

   public abstract double cost();

}

2个具体的饮料

import cn.design.decorate.inter.Beverage;

public class Espressoextends Beverage {

public Espresso(){

desc="Espresso";

}

@Override

public double cost() {

return 1.2;

}

//设置饮料描述

   public void setDesc(Stringdesc) {

     this.desc=desc;

 }

}

package cn.design.decorate;

import cn.design.decorate.inter.Beverage;

public class HouseBlendextends Beverage {

public HouseBlend() {

desc="HouseBlend";

}

@Override

public double cost() {

return 0.8;

}

//设置饮料描述

   public void setDesc(Stringdesc) {

     this.desc=desc;

 }

}

 

现在: 饮料抽象接口有了, 具体的饮料也有了,现在我们来实现装饰者

首先,来一个抽象接口

package cn.design.decorate.inter;

public abstract class CondimentDecorateextends Beverage{

  //从新定义描述行为

public abstract String getDescription();

}

 

 

 

具体的装饰者---调料

package cn.design.decorate;

import cn.design.decorate.inter.Beverage;

import cn.design.decorate.inter.CondimentDecorate;

public class Mochaextends CondimentDecorate{

    //被装饰者: 饮料

private Beverage beverage;

//设置饮料具体实现---但是Mocha装饰者不关心是那种饮料

public void setBeverage(Beveragebeverage) {

this.beverage =beverage;

}

//构造器,初始化beverage

public Mocha(Beveragebeverage) {

this.beverage=beverage;

}

//构造器: 也可以什么都不做

public Mocha() {

}

@Override

public String getDescription() {

//被装饰饮料描述 + 装饰者调料描述(可以通过设置变量来修改)

return beverage.getDescription()+",Mocha";

}

@Override

public double cost() {

//价钱: 被装饰饮料价钱 + 装饰者调料价钱

return beverage.cost()+ 0.6;

}

}

 

 

 

package cn.design.decorate;

import cn.design.decorate.inter.Beverage;

import cn.design.decorate.inter.CondimentDecorate;

public class Whipextends CondimentDecorate{

  //被装饰者: 饮料

private Beverage beverage;

//设置饮料具体实现---但是Whip装饰者不关心是那种饮料

public void setBeverage(Beveragebeverage) {

this.beverage =beverage;

}

//构造器,初始化beverage

public Whip(Beveragebeverage) {

this.beverage=beverage;

}

//构造器: 也可以什么都不做

public Whip() {

}

@Override

public String getDescription() {

//被装饰饮料描述 + 装饰者调料描述(可以通过设置变量来修改)

return beverage.getDescription()+",Whip";

}

@Override

public double cost() {

//价钱: 被装饰饮料价钱 + 装饰者调料价钱

return beverage.cost()+ 1.2;

}

}

 

 

 

 

一切顺利准备好了, 现在我们坐下来喝点饮料. 你想要什么调料的饮料?

package cn.design.decorate;

import cn.design.decorate.inter.Beverage;

/*

 *  “策略模式”

 * */

public class Client{

public static void main(String[] args) {

//(1)用Mocha装饰Espresso饮料

Beverage beverage=new Mocha(new Espresso());

        System.out.println("Description:"+beverage.getDescription());

        System.out.println("cost:"+beverage.cost());

        System.out.println("````````````````");

        //(2)Espresso饮料中含有Mocha, 再加点Whip会不会更加美味?

        beverage=new Whip(beverage);

        System.out.println("Description:"+beverage.getDescription());

        System.out.println("cost:"+beverage.cost());

        

        

}

}

 

 

 例子(2)是带有Mocha ,whip的Espresso饮料。我们的

Beverage beverage=new Mocha(new Espresso()); 想不想:  BufferedInputStreamis=new BufferedInputStream(new InputStream(){..});

 

总结: 代码终于写完了,简单运用了一下"装饰者模式",代码看起来好像舒服一些.

一  现在来看看, 我们的装饰者类是符合开闭原则的,再不要修改代码的情况下, 可以动态改变"被装饰者"来拓展行为. 

四 没有完美的设计模式--“装饰模式的不足之处”

Java的23种设计模式或多或少都是存在不足之处的,就像我们程序员,可能在某些技术或者管理方面存在不足, 但是这不影响我们使用模式. 装饰者模式不足之处应该也有,比如上面的例子中, 饮料要是在双十一打折呢?需要对具体的被装饰者编程时,由于在装饰者中修改了这个具体,使用了统一接口(接口中没有具体的函数定义)。这是要去"修改"代码吗?在实际开发中应该根据具体情况来处理, 而我们的"装饰者"的创建也可以交给工厂模式或者生成器管理!

 


猜你喜欢

转载自blog.csdn.net/huangchongwen/article/details/78687043