您的位置:首页 > 编程语言 > Java开发

设计模式学习笔记之装饰者模式

2017-02-26 17:29 459 查看
装饰者模式简单点说就是:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

设计关系图如下:

   


这样说,肯定比较抽象,下面我们来举个例子:

假如我想开个面馆(在显示生活中我真的想开个面馆啊!)。



    

为了能更好的赚钱,我肯定要推出各种各样的面类菜品。但其实基本全部的面都是由四种基本的面组成的:细面,宽面,刀削面和猫耳面。在这四种面的基础上,我们可以加入,青菜,鸡蛋,牛肉,组合出青菜细面,鸡蛋宽面,牛肉刀削面,青菜鸡蛋猫耳面等等。。现在为了方便我们的前台收银的服务员,我们来实现一个订单收银系统。



首先我们想到的设计思路肯定是先设计一个食物的父类然后为每一种不同品种的面条设计一种子类。比如,青菜刀削面类、牛肉刀削面类,鸡蛋细面类。。。。



这样设计的坏处就是扩展子类太多,不好维护,如果后期有新加入几种配菜品类,比如羊肉,番茄等等,这就会多出很多类,也不便于扩展。而且可能会出现类似于机器学习中的‘维度灾难’的类灾难的问题

如果我们对上面的想法来改进一下呢,我们把四种基本面都做成各自的类,然后把配菜做成各自的类,最后在计算总价的时候将他们相加。



这种设计虽然对比与第一种设计思想,不会出现类灾难的问题,但是当出现新的品类是还是需要修改主类中的部分功能比如:加和,所以还是不易维护的。

所以接下来就引入了我们的装饰者模式的设计方案了。

先给出装饰者的设计思路



基类,食物类

public abstract class Food {

//描述属性
public String description="";
//价格属性
private float price=0f;;

//设置描述
public void setDescription(String description)
{
this.description=description;
}

//获取描述
public String getDescription()
{
return description+"-"+this.getPrice();
}

//获取价格
public float getPrice()
{
return price;
}

//设置价格
public void setPrice(float price)
{
this.price=price;
}

//价格计算
public abstract float cost();

}

具体组件基类,面条类

public class Noodle extends Food {

//计算价格
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}
}

四种基本面类:
细面类

public class ThinNoodle extends Noodle {
public ThinNoodle()
{
super.setDescription("细面");
super.setPrice(6.0f);
}
}

宽面类

public class WideNoodle extends Noodle{

public WideNoodle()
{
super.setDescription("宽面");
super.setPrice(6.0f);
}
}


刀削面类

public class CutNoodle extends Noodle{

public CutNoodle()
{
super.setDescription("刀削面");
super.setPrice(7.0f);
}
}

猫耳面(原谅我可怜的英语吧。。)

public class CatEarNoodle extends Noodle{

public CatEarNoodle()
{
super.setDescription("猫耳面");
super.setPrice(8.0f);
}
}


装饰者基类
装饰者类

public class Decorator extends Food {

//食物类负责接受传进来的基本面条类并进行装饰
private Food Obj;

//构造函数
public Decorator(Food Obj){
this.Obj=Obj;
};

//价格计算
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice()+Obj.cost();
}

//得到描述信息
@Override
public String getDescription()
{
return super.description+"-"+super.getPrice()+"&&"+Obj.getDescription();
}
}

装饰者具体类
青菜类

public class Vegetables extends Decorator {

public Vegetables(Food Obj) {
super(Obj);
// TODO Auto-generated constructor stub
super.setDescription("青菜");
super.setPrice(1.5f);
}
}

鸡蛋类

public class Egg extends Decorator {

public Egg(Food Obj) {
super(Obj);
// TODO Auto-generated constructor stub
super.setDescription("鸡蛋");
super.setPrice(2.0f);
}
}

牛肉类

public class Beef extends Decorator {

public Beef(Food Obj) {
super(Obj);
// TODO Auto-generated constructor stub
super.setDescription("牛肉");
super.setPrice(5.0f);
}
}


由此我们可以看出,其实真正导致我们菜品价格变化的是装饰者的搭配不同,而且在当我们的面条基类生成好后,其实我们是对面条基类进行一层一层的装饰,并且因为这样的设计使得我们在计算价格的时候产生了一种类似于递归的计算方式,这样非常利用我们的新装饰者的扩展。

让我们写一个测试程序测试一下:

public class NoodleShop {
public static void main(String[] args) {

Food order;
order=new ThinNoodle();
System.out.println("一号订单价格:"+order.cost());
System.out.println("一号订单描述:"+order.getDescription());

System.out.println("#######################################");
order=new CutNoodle();
order=new Beef(order);
order=new Egg(order);
order=new Egg(order);
System.out.println("二号订单价格:"+order.cost());
System.out.println("二号订单描述:"+order.getDescription());

System.out.println("#######################################");
order=new CatEarNoodle();
order=new Beef(order);
order=new Egg(order);
order=new Vegetables(order);
System.out.println("三号订单价格:"+order.cost());
System.out.println("三号订单描述:"+order.getDescription());
}
}



看来我们非常成功的完成了我们的面店订单收银系统

那么我们来看看装饰者模式的优缺点和适用场景

优点:

    1.装饰者模式可以提供比继承更多的灵活性

    2.可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。

    3.通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

    4.具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开-闭原则”。
缺点:

    1.会产生很多的小对象,增加了系统的复杂性

    2.这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

适用场景:

    1. 需要扩展一个类的功能,或给一个类添加附加职责

    2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销

    3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息