Android中装饰者模式

设计模式系列:
        0. Android开发常用设计模式;

        1. Android中单例模式;

        2. Android中建造者(builder)模式;

        3. Android中观察者模式;

        4. Android中原型模式;

        5. Android中策略模式;

        6. Android中工厂模式;

        7. Android中代理模式;

        8. Android中装饰者模式;

        9. Android中适配器模式;



  三毛:“小白,你用过’饿了吗’app不”

  小白:嘿嘿,向我这样与时俱进的年轻人肯定用过吖 这里写图片描述


  三毛:“像下面的就是‘饿了么’某店的水果拼盘商品,见过吧”

  这里写图片描述


  小白:嗯嗯


一、常见需求场景


  三毛:假设现在某店分普通和VIP两种盒子装水果拼盘,现在只有下面4种水果套餐拼盘:

             (A)西瓜+哈密瓜
             (B)西瓜+葡萄
             (C)哈密瓜+葡萄
             (D)西瓜+哈密瓜+葡萄

顾客买了水果拼盘后就输出它的名字和价格,你来模拟下


二、基本解决方法

  小白:毛毛哥,我想到下面2种写法


方法1


//--------------------------水果类--------------------------//

//来个水果接口
public interface IFruit {
    String getFruitName();
    double getFruitPrice();
}

//西瓜
public class XiGua implements IFruit {
    @Override
    public String getFruitName() {
        return "西瓜";
    }

    @Override
    public double getFruitPrice() {
        return 2.00;
    }
}
//...其他哈密瓜,葡萄一样,这里就不写了


//--------------------------水果套餐类--------------------------//

//来个盒子接口
public interface IBox {
    void boxType();//盒子类型
    String getPackageName();//获取套餐名字
    double getPackagePrice();//获取套餐价格
}

//普通盒子装的水果拼盘套餐A
public class NormalBoxA implements IBox {
    private XiGua mXiGua;
    private HaMiGua mHaMiGua;

    public NormalBoxA() {
        mXiGua = new XiGua();
        mHaMiGua = new HaMiGua();
    }

    @Override
    public void boxType() {
        System.out.println("普通盒子装的");
    }

    @Override
    public String getPackageName() {
        boxType();
        return mXiGua.getFruitName()+"+"+mHaMiGua.getFruitName();
    }

    @Override
    public double getPackagePrice() {
        return mXiGua.getFruitPrice()+mHaMiGua.getFruitPrice()+0.5;
    }
}


//Vip盒子装的水果拼盘套餐A(和普通盒子没啥区别,价格贵了点)
public class VipBoxA implements IBox {
    private XiGua mXiGua;
    private HaMiGua mHaMiGua;

    public VipBoxA() {
        mXiGua = new XiGua();
        mHaMiGua = new HaMiGua();
    }

    @Override
    public void boxType() {
        System.out.println("Vip盒子装的");
    }

    @Override
    public String getPackageName() {
        boxType();
        return mXiGua.getFruitName()+"+"+mHaMiGua.getFruitName();
    }

    @Override
    public double getPackagePrice() {
        return mXiGua.getFruitPrice()+mHaMiGua.getFruitPrice();
    }
}

//...其他B、C、D套餐也是一样,这里就不重复了


//使用
 NormalBoxA normalBoxA = new NormalBoxA();
 VipBoxA vipBoxA = new VipBoxA();

 System.out.println(normalBoxA.getPackageName()+":"+normalBoxA.getPackagePrice());
 System.out.println(vipBoxA.getPackageName()+":"+vipBoxA.getPackagePrice());
 //其他套餐使用也一样...




方法2

//来个枚举或常量类(随便啦)
public enum FruitType {
    XiGua(2.00,"西瓜"),HaMiGua(6.00,"哈密瓜"),PuTao(8.50,"葡萄");

    private double price;
    private String name;

    FruitType(double price, String name) {
        this.price = price;
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public String getName() {
        return name;
    }
}


//------------------------------套餐类------------------------------//

//在来个套餐接口类
public interface IPackage {
    void boxType();//盒子类型
    String getPackageName();//得到套餐名字
    double getPackagePrice();//得到套餐价格
}


//来个普通盒子装的套餐实现类

public class NormalBoxPackageImpl implements IPackage {
    private double mPrice;
    private String mFruitPackageName = "";

    public void addXiGua() {
        mPrice += FruitType.XiGua.getPrice();
        mFruitPackageName += FruitType.XiGua.getName() + "+";
    }

    public void addHaMiGua() {
        mPrice += FruitType.HaMiGua.getPrice();
        mFruitPackageName += FruitType.HaMiGua.getName() + "+";
    }

    public void addPuTao() {
        mPrice += FruitType.PuTao.getPrice();
        mFruitPackageName += FruitType.PuTao.getName() + "+";
    }


    @Override
    public void boxType() {
        System.out.println("普通盒子装的");
    }

    @Override
    public String getPackageName() {
        boxType();
        return mFruitPackageName.isEmpty() ? "" : mFruitPackageName.substring(0, mFruitPackageName.length() - 1);
    }

    @Override
    public double getPackagePrice() {
        return mPrice;
    }
}

//另外VipBoxPackageImpl类省略,vip盒子装的和普通盒子装的没多少区别,只是套餐价格贵了点...


//使用

 NormalBoxPackageImpl normalBoxPackage= new NormalBoxPackageImpl();
 normalBoxPackage.addHaMiGua();//要什么水果套餐,自己拼就OK了
 System.out.println(normalBoxPackage.getPackageName()+":"+normalBoxPackage.getPackagePrice());



  三毛:“小白,可以吖,鬼点子挺多的嘛”

  小白:还行,还行这里写图片描述

三、基本解决方法存在的问题


  三毛:“鬼点子多是多,但是上面的两种方法都存在相应的问题喔”

  小白:这里写图片描述

  三毛:”不相信的话,那我来分析分析给你听,我们是讲道理的嘛”

  小白:好吧,毛毛哥你分析分析,我听听看

  三毛:“方法1,白,假设我多加5种水果,套餐就达到26种,有2种盒子,那你不得写26*2个套餐类喔,而且我说的只是5种,如果更加多的话,岂不是累死你了”

  小白:这里写图片描述有点道理

  三毛:”方法2,这个写法确实没有方法1那种问题了,但是,如果功能扩展的时候你是不是得在NormalBoxPackageImpl或VipBoxPackageImpl类里做修改,比如加多个火龙果,你就加多个火龙果方法,套餐中有火龙果的话,你就相当于改变了价格和套餐名字(你要计算价格…),很明显违反了设计模式六大原则中的开闭原则的闭,也就是,新增功能的时候,不能去修改原有的代码”

  小白:我觉得第二种方法挺好的吖,简单粗暴,违反就违反呗

  三毛:”我只是和你说你那样写存在那样的问题,并不是说不能用,其实看场景和需求考虑就好”

  小白:我就是这样想滴,毛毛哥哥

  三毛:”小白,如果装饰者模式能像你上面方法2一样,但是又不违反设计模式规则,你觉得怎么样”

  小白: 这里写图片描述  快给我讲讲,毛毛哥


四、装饰者模式写法

装饰者模式定义:动态给一个对象添加一些额外的职责。装饰着模式相比用生成子类方式达到功能的扩展显得更加灵活。


  三毛:”睁大眼睛看好了,使用装饰者模式完成上面需求的正确姿势”

//装饰者模式标准写法,分为4个部分 -->(1)接口;(2)实现接口;(3)抽象类;(4)实现抽象类。

//先来个接口
public interface IGoods {
    String getName();//获取商品名字
    double getPrice();//获取商品价格
}


//实现商品接口的西瓜
public class XiGua implements IGoods {

    private IGoods mIGoods;

    public XiGua(IGoods mIGoods) {
        this.mIGoods = mIGoods;
    }

    @Override
    public String getName() {
        return null == mIGoods ?"西瓜":mIGoods.getName() + "+西瓜";
    }

    @Override
    public double getPrice() {
        return null == mIGoods ?2.00:mIGoods.getPrice() + 2.00;
    }

}
//哈密瓜、普通...和上面一样,代码省略...



//来个套餐抽象类
public abstract class AbstractPackage implements IGoods {

    public IGoods mIGoods;
      //你也可以在这里加一些商品接口没有的抽象方法,实现扩展...

    public AbstractPackage(IGoods mIGoods) {
        this.mIGoods = mIGoods;
    }
     //获取套餐(商品)名字
    public String getPackageName() {
        return mIGoods.getName();
    }
     //获取套餐(商品)价格
    public double getPackagePrice() {
        return mIGoods.getPrice();
    }
}


//实现抽象类 -->普通盒子装的水果套餐类
public class NormalBox extends AbstractPackage {

    NormalBox(IGoods mIGoods) {
        super(mIGoods);
    }

    @Override
    public String getName() {
        return "普通盒子装的:" + super.getPackageName();
    }

    @Override
    public double getPrice() {
        return 0.5 + super.getPackagePrice();
    }

}



//实现抽象类 -->Vip盒子装的水果套餐类
public class VipBox extends AbstractPackage {


    public VipBox(IGoods mIGoods) {
        super(mIGoods);
    }

    @Override
    public String getName() {
        return "Vip盒子装的:" + super.getPackageName();
    }

    @Override
    public double getPrice() {
        return 1.5 + super.getPackagePrice();
    }
}


//使用
 XiGua xiGua = new XiGua(null);
 HaMiGua haMiGua = new HaMiGua(xiGua);//随意组装你想要的水果(拼盘)
 NormalBox normalBox = new NormalBox(haMiGua);//放到你想要的盒子里
 System.out.println(normalBox.getName() + ":" + normalBox.getPrice());



  小白:使用装饰者模式后,不管套餐有几百个,我们只需要增加对应的水果,然后自己随心所欲组合就好了,而且相互独立不影响 这里写图片描述

  三毛:”嗯,装饰者模式就像穿衣服,一层套一层,先穿什么,在穿什么都可以,只要是衣服(实现同一接口)类型”

  小白:呐,毛毛哥,装饰者模式一定要按上面的标准写四个东西嘛

  三毛:”我的白,你不要误解喔,上面只是标准做法,实际当中你要根据业务来做,虽然模式是这样,但是只要你代码写得好就行,管它怎样讷,比如上面的普通盒子和Vip盒子类我可以不按上面那样写,可以像下面这样写”

//不实现抽象类了,改成实现商品接口
public class NormalBox implements IGoods {
    public IGoods mIGoods;

    public NormalBox(IGoods mIGoods) {
        this.mIGoods = mIGoods;
    }

    @Override
    public String getName() {
        return "普通盒子装的:" + mIGoods.getName();
    }

    @Override
    public double getPrice() {
        return 0.5 + mIGoods.getPrice();
    }

}
//Vip盒子实现类也可以换成这样写....

//使用还是没变化...

  小白:”这样写比标准写法少了个抽象类啊,这样不会有什么影响嘛”

  三毛;”多个套餐抽象类看起来比较清晰明了啊,如果非要说有影响的话,就是当你扩展业务时,比如在使用盒子装水果套餐前询问用户是否使用本店优惠券,你还不是得在多加一个接口或抽象类?不过我认为怎么写没关系,模式只是提供你一些思路,写代码考虑好问题就没毛病”

  小白: 这里写图片描述

五、装饰者模式和普通写法区别


1、结构清晰,耦合度更低,扩展性更强;

2、符合设计原则中的开闭原则;

3、比某些正常写法少了很多类和工作;

4、随心所欲按你的顺序组合,灵活多变。

猜你喜欢

转载自blog.csdn.net/u014769864/article/details/78439018
今日推荐