美文网首页
设计模式之装饰器模式

设计模式之装饰器模式

作者: 天若有情天亦老 | 来源:发表于2017-12-15 16:33 被阅读55次

0x01 前言


在编码的时候,我们为了扩展一个类常用的方法就是继承,随着扩展功能越来越多,子类会变得越来越膨胀,
使系统变得不灵活。

装饰器模式(Decorator pattern)允许像一个现有的对象添加新功能,同时又不改变其结构,他能让我们在扩展类的同时,系统保持较好的灵活性。

0x02 废话不多说,进入正题,从一个场景开始


假设我们在北京有一块地,在这块地上我们要盖有好几间房间的别墅,每个房间的装修费用都不同,现在呢,我们需要对每个房间的费用进行计算并计算出别墅的价格。

//定义一个land表示这块地
public interface Land {
     // 定义盖别墅需要花钱的规则
    Integer cost();
}

那么land定义好了在这块地上花钱的规则,但是盖一间房需要多少钱呢?

此时我们定义一个Room类,这个类定义了一间房间需要建造的基本费用。

public class Room implements Land{
    
    protected final int money = 1000 ;

    @Override
    public Integer cost() {
        return money ;
    }

}

开始建造房间,房间建造了两间,一间客厅(LivingRoom),一间餐厅(DiningRoom),建造完之后发现偌大房间连件家具都没有,这时候,我们计算一间房成本的时候需要加上家具的价格,那么,上代码...

public class LivingRoom extends Room{
    // 客厅
    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}

public class DiningRoom extends Room{
    // 餐厅
    @Override
    public void cost() {
        return this.land.cost()+100;
    }
}

所以机智如你很容易就得到了建造一间客厅和一间餐厅的花费..

public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom();
        System.out.println("建造一间客厅需要花费"+livingRoom.cost());
        DiningRoom diningRoom = new DiningRoom();
        System.out.println("建造一间餐厅需要花费"+diningRoom.cost());
    }
// 得到结果
建造一间客厅需要花费1200
建造一间餐厅需要花费1100

那么,问题来了:挖掘机...(啪,好好说啊,挖掘机是什么鬼啊,喂)emmmmm,好接着说:

0x03 问题的产生


这么做实现确实很容易,虽然我们很容易就得到了建造一间客厅和一间餐厅需要的花费,但是这样的结构不具备灵活性,如果....我是说如果我的地比较小不能同时建造两间房子,只能把客厅和餐厅建在同一个屋子里,那么该去怎么计算费用?难道,还要很麻烦得去创建一个包含LivingDiningRoom类吗?这样做除了麻烦,而且还会使代码重复。

既然提出了问题,那么,肯定有解决的办法,怎么解决呢?
欲知后事如何..(pia,知你妹啊,还来?),好吧,下面是解决办法。

0x04 解决问题


(敲黑板!!)重点来了,为了更好的解决问题,我们需要做一些调整,同样先声明Land接口(抽象构件,是具体构件和抽象构件的共同父类)和Room类( ConcreteComponent ,具体构件,抽象构件的子类,装饰器可以额外添加职责。),不同的是引入房间的一个装饰类RoomDecorator他实现了Land接口,因为没有实现Land的cost方法,所以需要声明为抽象类,并且定义一个Land接口为参数的构造函数,传入对象会保存在land中,该属性修饰符为protected方便子类访问。(划重点,考试要考)

public abstract class RoomDecorator implements Land{
    // 修饰符protected子类访问
    protected Land land ;
    
    public RoomDecorator(Land land) {  // 参数为实现Land接口的所有子类
        this.land = land ;
    }
}

我们重新定义客厅类和餐厅类

public class LivingRoom extends RoomDecorator{

    public LivingRoom(Land land) {
        super(land);
    }

    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}

public class DiningRoom extends RoomDecorator{

    public DiningRoom(Land land) {
        super(land);
    }

    @Override
    public Integer cost() {
        return this.land.cost()+100;
    }
}

这两个类都继承了RoomDecorator,这意味着它们同时拥有指向Land接口的引用,当它们的cost()方法被调用时,都会先调用实现了Land接口的子类的cost()方法,然后执行自己特有的操作。

所以这时候建造一间客厅和一间餐厅所需的费用是这么计算的:

public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom(new Room());
        System.out.println("建造一间客厅需要花费"+livingRoom.cost());
        
        DiningRoom diningRoom = new DiningRoom(new Room());
        System.out.println("建造一间餐厅需要花费"+diningRoom.cost());
    }
// 输出
建造一间客厅需要花费1200
建造一间餐厅需要花费1100

接着回到刚才的问题:建造一间有客厅又有餐厅的房间所需费用,代码如下

public static void main(String[] args) {
        LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
        System.out.println("建造一间客厅和餐厅需要花费"+diningLivingRoom.cost());
    }
// 输出
建造一间客厅和餐厅需要花费1300

我们计算建造费用的思路是:我们计算一间房的基础费用-->在基础房间装饰为客厅的费用-->在客厅的基础上加上装饰餐厅的费用-->得到包含客厅餐厅房间的费用,已经不需要通过麻烦的创建LivingDiningRoom类来计算包含餐厅客厅房间的建造费用了。

这便是装饰模式,通过一层层的装饰得到我们可以灵活的得到想要的结果,可以轻松地添加组件或者装饰类来创建灵活的结构。

0x05 完整代码

//定义一个land表示这块地
//  Component:组件对象的接口,可以给这些对象动态的添加职责
public interface Land {
     // 定义盖别墅需要花钱的规则
    Integer cost();
}
// ConcreteComponent:具体的组件对象,实现了组件接口。该对象通常就是被装饰器装饰的原始对象,可以给这个对象添加职责;
public class Room implements Land{
    protected final int money = 1000 ;
    @Override
    public Integer cost() {
        return money;
    }
}

// Decorator:所有装饰器的父类,需要定义一个与组件接口一致的接口
public abstract class RoomDecorator implements Land{
    // 修饰符protected子类访问
    protected Land land ;
    public RoomDecorator(Land land) {
        this.land = land ;
    }
}

// 装饰类具体构件
public class LivingRoom extends RoomDecorator{
    public LivingRoom(Land land) {
        super(land);
    }
    @Override
    public Integer cost() {
        return this.land.cost()+200;
    }
}
// 装饰类具体构件
public class DiningRoom extends RoomDecorator{
    public DiningRoom(Land land) {
        super(land);
    }
    @Override
    public Integer cost() {
        return this.land.cost()+100;
    }
}

// 测试
public class RoomTest{
    public static void main(String[] args) {
        LivingRoom livingRoom = new LivingRoom(new Room());
        System.out.println("建造一间客厅需要花费"+livingRoom.cost());
        
        DiningRoom diningRoom = new DiningRoom(new Room());
        System.out.println("建造一间餐厅需要花费"+diningRoom.cost());
        
        LivingRoom diningLivingRoom = new LivingRoom(new DiningRoom(new Room()));
        System.out.println("建造一间客厅和餐厅需要花费"+diningLivingRoom.cost());
    }
}

0x06 The End...


Tips:在北京有块地一辈子都不可能的,如果可以,我愿意偷电动车养你,点颗心在走嘛。

相关文章

网友评论

      本文标题:设计模式之装饰器模式

      本文链接:https://www.haomeiwen.com/subject/sxqdwxtx.html