设计模式 -- 装饰模式

Decorator Pattern装饰模式

装饰模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

装饰,让我们想到了房子的装修、人的打扮、手机装饰等等,但是,不管怎么装修,房子还是那个房子、墙还是那堵墙,人还是那个人,脸还是那张脸;手机还是那部手机,屏幕还是那个屏幕,只是给人的感觉变了而已。

如果拿手机来说,我们买了手机后,都会给手机贴膜、安个吊坠、装个边框什么的,下面我们看看我们最常用的手机是如何贴膜的:


 

 

public class Phone {
    //构造方法
    public Phone(String name) {
        this.name = name;
    }
    //名称
private String name;
//贴膜
    public void film(){
        System.out.print(" 贴膜 "); 
}
//最后的外观
    public void exterior(){
        System.out.println(" 手机:"+ name + "的样子!"); 
    }
    //getter & setter
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

 

public class PhoneTest {
    public static void main(String[] args) {
        Phone p = new Phone("Iphone5");//一台Iphone5
        p.film();       //贴膜
        p.exterior();   //我们看到的样子
    }
}
 

 

这样设计是满足了要求,可是大家都知道,手机装饰有很多种,除了贴膜,还有吊坠、边框、后盖、贴纸….等等,难道新需求来的时候,去修改Phone这个类吗?

根据我们的设计原则:“只对扩展开放,对修改关闭”。就是根据变化去设计一个程序,以后有变化的时候,之扩展我们的程序就行了,不需要修改原来的程序。这就是设计模式的精髓所在。

根据以上的例子,我们要考虑,手机装饰随时可能变化,当你逛街时、浏览某个网站时,看到一款比较新奇的手机配件,也许你会更换装饰了!

我们可能想到了接口:把手机的装饰用一个接口来表示:让所有手机配件来继承这个接口:

 


 

 

 

//手机

public class Phone {
    //构造方法
    public Phone(String name) {
        this.name = name;
    }
    //名称
    private String name;
    //外观
    private List<Accessories> accessories = new ArrayList<Accessories>();
//   外观展示
    public void exterior(){
        for(Accessories accessorie : accessories){
            accessorie.exterior();
        }
        System.out.println(" 手机:"+ name + "的样子!");
    }
    //getter & setter
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Accessories> getAccessories() {
        return accessories;
    }
    public void setAccessories(List<Accessories> accessories) {
        this.accessories = accessories;
    }
}
 

 

//配件接口
public interface Accessories {
    void exterior();
} 
//贴膜
public class Film implements Accessories {
    public void exterior() {
        System.out.print(" 贴膜 "); 
    }
}
 //金属边框
public class MetalFrame implements Accessories {
    public void exterior() {
        System.out.print(" 金属边框 "); 
    }
}
 

 

//手机测试
public class PhoneTest {
    public static void main(String[] args) {
        Phone p = new Phone("Iphone5");//一台Iphone5
        //配件
        Accessories a = new Film();
        Accessories b = new MetalFrame();
        List<Accessories> l = new ArrayList<Accessories>();
        l.add(a);
        l.add(b);
        p.setAccessories(l);
//        外观展示
        p.exterior();
    }
}
 

 

这样似乎解决了问题,可是代码看起来非常复杂,特别是配件在装饰的时候,都要装到配件列表中。有没有更简洁的方式呢?

好吧,还是让我们用装饰模式优雅的解决这个问题吧:

 先看类图:


 

 

代码如下:

 

//手机接口
public interface Phone {
    public void exterior();
}
 

 

//具体的对象  --实现了手机接口
public class Iphone5 implements Phone {
    public void exterior() {
        System.out.print("Iphone5外观展示!"); 
    }
}

 //手机配件抽象类 ---同样实现手机接口

public abstract class Accessories implements Phone {
    //要装饰的对象 -- 手机
    private Phone phone;
    public void setPhone(Phone phone) {
        this.phone = phone;
    }
    //调用传入的Phone的exterior方法
    public void exterior() {
        if(phone != null){
            phone.exterior();
        }
    }
}
 

 

//具体装饰类:贴膜  -- 继承自配件类
public class Film extends Accessories {
//    重写父类中的外观展示方法,
//    在父类方法调用前加入自己的处理逻辑
    public void exterior() {
        System.out.print(" 贴膜 "); 
        super.exterior();
    }
}
 

 

 

//具体装饰:金属边框 – 继承自配件类
public class MetalFrame extends Accessories {
//  重写父类中的外观展示方法
//  在父类方法调用前加入自己的处理逻辑
    public void exterior() {
        System.out.print(" 金属边框 ");
        super.exterior();
    }
}
 

 

//代码测试
public class PhoneTest {
    public static void main(String[] args) {
//      一台Iphone5
        Iphone5 iphone5 = new Iphone5();
        //配件
        Film film = new Film();
        MetalFrame metalFrame = new MetalFrame();
//        外观展示
        film.setPhone(iphone5);
        metalFrame.setPhone(film);
        metalFrame.exterior();
    }
}
 

 

      是不是感觉代码更加简洁了,而且也更加灵活了。这是装饰模式在程序设计中发挥的重要作用。

      如果还不明白,我们可以举一个非常简单的例子,我们都吃过火锅,比如呷哺火锅,火锅点餐系统就是你点一份锅底,然后选择几份配菜,配菜可以随便点、而且可以点多份,锅底就一个,到结账的时候要知道点了那些东西,然后得花多少钱。根据这个需求,我们设计的时候用装饰模式来解决:

         建立一个食品接口,有一个获得食品名称的方法,一个计算价格的方法和另外一个通过传入其它食品计算价格的方法,然后具体对象是锅底,因为菜都是在锅底里面涮的,没有锅底肯定不行,而配菜是跟随锅底随意搭配的,配菜可以多点,而且可以点多份。建一个锅底类,给出具体名称和具体价格,和一个配菜抽象类,让锅底类和配菜都继承这个食品接口,然后让具体的配菜都继承配菜类,在配菜里给出配菜具体名称和具体价格。类图如下:


 

 

 

这样设计的好处是锅底的配菜能随意搭配,可多选可复选,而且以后如果增加另外一种锅底的时候,只需要去继承这个食品接口就可以了,配菜在扩展的时候只需要把新上市的蔬菜去继承配菜就行了,并不需要去更改以前写好的代码,这就遵循了设计原则的:“针对接口编程”和“对扩展开放,对修改关闭”的原则了。看看下面的代码,让我们开始点餐吧:

 

 

 

//食品接口
public interface Food {
    String getName();
    int price();
    int price(Food food);
}
 

 

//配菜抽象类
public abstract class Materials implements Food {
    public abstract String getName();
}
 

 

 

//锅底类—具体对象
public class Spicy implements Food {
    private String name;
    //构造方法
    public Spicy(String name){
        this.name = name;
    }
    //食品名称
    public String getName(){
        return name;
    }
    //价格
    public int price() {
        return 15;
    }
    public int price(Food food) {
        return food.price() + 15;
    }
}
 

 

 

//粉丝  --继承自配菜
public class Fans extends Materials {
    private Food food;
    //构造方法
    public Fans(Food food) {
        this.food = food;
    }
    public String getName() {
        return food.getName() + ",粉丝";
    }
    public int price() {
        return food.price() + 3;
    }
    public int price(Food food) {
        return food.price() + 3;
    }
}
 

 

 

//白菜类 – 继承自配菜
public class Cabbage extends Materials {
    private Food food;
    //构造方法
    public Cabbage(Food food) {
        this.food = food;
    }
    public String getName() {
        return food.getName() + ",白菜";
    }
    //白菜价格
    public int price() {
        return food.price() + 2;
    }
    //
    public int price(Food food) {
        return food.price() + 2;
    }
}
 

 

//牛肉类 – 继承自配菜
public class Beef extends Materials {
    private Food food;
    //构造方法
    public Beef(Food food) {
        this.food = food;
    }
    public String getName() {
        return food.getName() + ",牛肉";
    }
    public int price() {
        return food.price() + 10;
    }
    public int price(Food foods) {
        return foods.price() + 10;
    }
}
 
//五花肉类 – 继承自配菜
public class Pork extends Materials {
    private Food food;
    //构造方法
    public Pork(Food food) {
        this.food = food;
    }
    public String getName() {
        return food.getName() + ",五花肉";
    }
    public int price() {
        return food.price() + 8;
    }
    public int price(Food food) {
        return food.price() + 8;
    }
}
 
//豆皮类 – 继承自配菜类
public class Yuba extends Materials {
    private Food food;
    //构造方法
    public Yuba(Food food) {
        this.food = food;
    }
    public String getName() {
        return food.getName() + ",豆皮";
    }
    public int price() {
        return food.price() + 5;
    }
    public int price(Food foods) {
        return foods.price() + 5;
    }
}
//测试类
public class FoodTest {
    public static void main(String[] args) {
//      香辣锅底的点餐
        Spicy spicy = new Spicy("香辣锅底");
        Fans fans = new Fans(spicy);
        Yuba yuba = new Yuba(fans);
        Beef beef = new Beef(yuba);
        Cabbage cabbage = new Cabbage(beef);
        System.out.println( "您要点的菜是:" + cabbage.getName() 
+ ",合计:" + cabbage.price()+ "元。");
        
//      清汤锅底的点餐
        Spicy spicy2 = new Spicy("清汤锅底");
        Pork pork = new Pork(spicy2);
        Fans fans2 = new Fans(pork);
        Fans fans3 = new Fans(fans2);
        Cabbage cabbage2 = new Cabbage(fans3);
        System.out.println( "您要点的菜是:" + cabbage2.getName() 
+ ",合计:" + cabbage2.price()+ "元。");
    }
}

看看装饰模式的定义:动态的给一共对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

         在看看我们的程序,锅底对象可以动态的添加配菜,并且能动态的计算出当前点餐的价格。比起生成一个单独配菜的子类然后计算价格更加灵活。

猜你喜欢

转载自chwshuang.iteye.com/blog/1612241