概念
- 装饰器设计模式,也叫包装设计模式,他是作为现有类的一个包装,允许向一个现有的对象添加新的功能,同时又不改变其结构。
- 给对象添加功能,一般两种方式,继承或者组合,将一个类的对象嵌入到另一个对象中,由另一个对象来决定是否调用嵌入对象的行为来增强功能,这个就是装饰器模式,比继承更加灵活。
- 装饰器设计模式中几个角色
- 抽象组件(Component):定义装饰方法的规范
- 被装饰者(ConcreteComponent):Component的具体实现,也就是具体的装饰对象
- 装饰者组件(Decorator):定义具体装饰者的行为规范,和Component角色有相同的接口,持有组件(Component)对象的实例引用
- 具体装饰物(ConcreteDecorator):负责给构建对象装饰附加功能
应用场景
对于装饰器设计模式上面概念介绍看起来属实是比较抽象的,举两个通俗易解的例子。
场景一:小明由于预算有限,但是也想买一台能吃鸡的电脑,所以小明决定自己去当地数码一条街自己买配件组装一台价格低廉能吃鸡的台式机。主板、CPU、显卡、电源、硬盘内存等等,只要购买全所有配件,小明便可以自己组装一台自己心仪的电脑。
电脑即抽象组件;ROG电脑、联想电脑或戴尔电脑就是被装饰者;CPU和显卡即装饰者组件,i5 12100、RTX2060这就是具体装饰物,如下图概述了几个角色间的关系。(图其实不太准确,被装饰组件应该拆分成CPU、GPU、主版本等,具体装饰就是具体型号的物品了)
场景二:最近比较热门的割草游戏《brotato》又名《土豆兄弟》,如果玩过土豆兄弟这款游戏的或者类似割草游戏的应该比较清楚,其中道具部分我们也可以通过装饰器设计模式去完成游戏这部分内容。英雄即为抽象组件;具体英雄如多面手、独臂、幸运星等是被装饰者;道具是抽象组件;具体道具如翅膀、存钱罐、手铐就是具体装饰。玩家在游玩过程拾取和购买各种道具,通过这些不同道具给角色增加各种属性加成,使得玩家能够支撑20波,最终消灭BOSS。
JAVA代码实现
就上面买电脑的场景,我们来用Java去实现买配件组装电脑的过程
抽象组件电脑
Computer.java
public interface Computer {/*** 名称** @return*/String getDesc();/*** 价格** @return*/int getPrice();}
被装饰者
这里列举三个(其实这里面机箱应该提取出来作为装饰组件的,这里偷懒了,直接和品牌挂钩了)
DiyComputer.java
/*** DIY台式机*/
public class DiyComputer implements Computer {@Overridepublic String getDesc() {return "DIY台式机";}@Overridepublic int getPrice() {// DIY机箱便宜些,默认为300return 300;}
}
LegionComputer.java
/*** 联想拯救者台式机*/
public class LegionComputer implements Computer {@Overridepublic String getDesc() {return "DIY台式机";}@Overridepublic int getPrice() {// 拯救者机箱500return 500;}
}
RogComputer.java
/*** 玩家国度台式机*/
public class RogComputer implements Computer {@Overridepublic String getDesc() {return "ROG台式机";}@Overridepublic int getPrice() {// ROG机箱1500return 1500;}
}
被装饰组件
ComputerComponent.java
/*** 电脑配件基类*/
public class ComputerComponent implements Computer {protected Computer computer;protected String desc = "";protected int price = 0;public ComputerComponent(Computer computer, CompunentEnum compunentEnum) {this.computer = computer;this.desc = compunentEnum.getType();this.price = compunentEnum.getPrice();}@Overridepublic String getDesc() {return computer.getDesc() + "\r\n" + desc;}@Overridepublic int getPrice() {return computer.getPrice() + price;}}
具体装饰
下面是台式机电脑的6个配件,组装成一个台式机远不是下面这些,这里只是把比较核心和大家比较关注的拿出来了。
CPU组件:CpuComponent.java
/*** 电脑组件:CPU*/
public class CpuComponent extends ComputerComponent {public CpuComponent(Computer computer, CpuEnum cpuEnum) {super(computer, cpuEnum);}
}
磁盘组件:DiskComponent.java
/*** 电脑组件:磁盘*/
public class DiskComponent extends ComputerComponent {public DiskComponent(Computer computer, DiskEnum diskEnum) {super(computer, diskEnum);}
}
显卡组件:GpuComponent.java
/*** 电脑组件:显卡*/
public class GpuComponent extends ComputerComponent {public GpuComponent(Computer computer, GpuEnum gpuEnum) {super(computer, gpuEnum);}
}
主板组件:MainBoardComponent.java
/*** 电脑组件:主板*/
public class MainBoardComponent extends ComputerComponent {public MainBoardComponent(Computer computer, MainBoardEnum mainBoardEnum) {super(computer, mainBoardEnum);}
}
电源组件:PowerComponent.java
/*** 电脑组件:电源*/
public class PowerComponent extends ComputerComponent {public PowerComponent(Computer computer, PowerEnum powerEnum) {super(computer, powerEnum);}
}
内存组件:RamComponent.java
/*** 电脑组件:内存*/
public class RamComponent extends ComputerComponent {public RamComponent(Computer computer, RamEnum ramEnum) {super(computer, ramEnum);}
}
其他
这里方便组件的创建,把常见的配件型号和价格做成一个枚举。
枚举基类:CompunentEnum.java
public interface CompunentEnum {String getType();int getPrice();}
CpuEnum.java
public enum CpuEnum implements CompunentEnum {CPU_I3_12100("i3 12100", 1100),CPU_I5_12600("i5 12600", 1900),CPU_I7_12700("i7 12700", 2600),CPU_I9_12900("i9 12900", 3900);private String type;private int price;CpuEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
DiskEnum.java
public enum DiskEnum implements CompunentEnum {WD_SATA_1T("西部数据(WD)蓝盘 1TB", 300),WD_SATA_2T("西部数据(WD)蓝盘 2TB", 380),WD_SATA_2T_NEW("西部数据(WD)蓝盘 2TB 新", 400),WD_SATA_3T("西部数据(WD)蓝盘 3TB", 430),WD_SATA_4T("西部数据(WD)蓝盘 4TB", 580),WD_SATA_6T("西部数据(WD)蓝盘 6TB", 960),WD_SATA_8T("西部数据(WD)蓝盘 6TB", 1310),WD_SSD_240G("西部数据(WD) Green SSD固态硬盘 240G", 170),WD_SSD_480G("西部数据(WD) Green SSD固态硬盘 480G", 270),WD_SSD_1T("西部数据(WD) Green SSD固态硬盘 1T", 530),WD_SSD_2T("西部数据(WD) Green SSD固态硬盘 2T", 1100),ST_SATA_1T("希捷(Seagate)1TB", 300),ST_SATA_2T("希捷(Seagate)2TB", 380),ST_SATA_4T("希捷(Seagate)4TB", 550),ST_SATA_6T("希捷(Seagate)6TB", 820),ST_SATA_8T("希捷(Seagate)8TB", 1200),ST_SSD_500G("希捷(SEAGATE) SSD固态硬盘 500G", 300),ST_SSD_1T("希捷(SEAGATE) SSD固态硬盘 1T", 550),ST_SSD_2T("希捷(SEAGATE) SSD固态硬盘 2T", 1130),SAMSUNG_SSD_870_QVO_1T("三星(SAMSUNG)2TB SSD固态硬盘 SATA3.0接口 870 QVO 1T", 600),SAMSUNG_SSD_870_QVO_2T("三星(SAMSUNG)2TB SSD固态硬盘 SATA3.0接口 870 QVO 2T", 1500),SAMSUNG_SSD_870_QVO_4T("三星(SAMSUNG)2TB SSD固态硬盘 SATA3.0接口 870 QVO 4T", 3000),SAMSUNG_SSD_870_QVO_8T("三星(SAMSUNG)2TB SSD固态硬盘 SATA3.0接口 870 QVO 8T", 5800),SAMSUNG_SSD_870_EVO_256G("三星(SAMSUNG)250GB SSD固态硬盘 SATA3.0接口 870 EVO 256G", 240),SAMSUNG_SSD_870_EVO_512G("三星(SAMSUNG)250GB SSD固态硬盘 SATA3.0接口 870 EVO 512G", 400),SAMSUNG_SSD_870_EVO_1T("三星(SAMSUNG)250GB SSD固态硬盘 SATA3.0接口 870 EVO 1T", 800),SAMSUNG_SSD_870_EVO_2T("三星(SAMSUNG)250GB SSD固态硬盘 SATA3.0接口 870 EVO 2T", 2000),SAMSUNG_SSD_870_EVO_4T("三星(SAMSUNG)250GB SSD固态硬盘 SATA3.0接口 870 EVO 4T", 3700),SAMSUNG_SSD_970_EVO_PLUS_256G("三星(SAMSUNG)250GB SSD固态硬盘 M.2接口(NVMe协议) 970 EVO Plus 256G", 350),SAMSUNG_SSD_970_EVO_PLUS_512G("三星(SAMSUNG)250GB SSD固态硬盘 M.2接口(NVMe协议) 970 EVO Plus 512G", 470),SAMSUNG_SSD_970_EVO_PLUS_1T("三星(SAMSUNG)250GB SSD固态硬盘 M.2接口(NVMe协议) 970 EVO Plus 1T", 900),SAMSUNG_SSD_970_EVO_PLUS_2T("三星(SAMSUNG)250GB SSD固态硬盘 M.2接口(NVMe协议) 970 EVO Plus 2T", 1700),SAMSUNG_SSD_980_PRO_500G("三星(SAMSUNG)1TB SSD固态硬盘 M.2接口(NVMe协议PCIe 4.0 x4) 980 PRO 500G", 600),SAMSUNG_SSD_980_PRO_1T("三星(SAMSUNG)1TB SSD固态硬盘 M.2接口(NVMe协议PCIe 4.0 x4) 980 PRO 1T", 1000),SAMSUNG_SSD_980_PRO_2T("三星(SAMSUNG)1TB SSD固态硬盘 M.2接口(NVMe协议PCIe 4.0 x4) 980 PRO 2T", 2200),SAMSUNG_SSD_980_PRO_HEATSINT_1T("三星(SAMSUNG)1TB SSD固态硬盘 M.2接口(NVMe协议PCIe 4.0 x4) 980 PRO With Heatsink(散热片版) 1T", 1100),SAMSUNG_SSD_980_PRO_HEATSINT_2T("三星(SAMSUNG)1TB SSD固态硬盘 M.2接口(NVMe协议PCIe 4.0 x4) 980 PRO With Heatsink(散热片版) 2T", 2300);private String type;private int price;DiskEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
GpuEnum.java
public enum GpuEnum implements CompunentEnum {COLORFUL_GEFORCE_RTX_2060("七彩虹(Colorful) RTX 2060", 1700),COLORFUL_GEFORCE_RTX_3060_DUO("七彩虹(Colorful)战斧 GeForce RTX 3060 DUO 8GB", 2400),COLORFUL_GEFORCE_RTX_3060_TI("七彩虹(Colorful)iGame GeForce RTX 3060 Ti Ultra W OC G6X V2", 3600),COLORFUL_GEFORCE_RTX_3070_TI("七彩虹(Colorful)iGame GeForce RTX 3070 Ti Ultra W OC 8G", 4500),COLORFUL_GEFORCE_RTX_4090_TI("七彩虹(Colorful)水神iGame GeForce RTX 4090 Neptune OC 24G", 15000),ASUS_RX_6500XT("华硕 ASUS DUAL-RX6500XT-O4G", 1400),ASUS_GTX_1660S("华硕 (ASUS)TUF-GeForce GTX 1660S-O6G-GAMING", 1700),ASUS_RTX_2060("华硕 ASUS DUAL GeForce RTX 2060-O6G-EVO", 2300),ASUS_RTX_3060("华硕 ASUS TUF GeForce RTX 3060-O12G-V2-GAMING", 3200),ASUS_RTX_3070_TI("华硕 ASUS TUF GeForce RTX 3070 Ti-O8G-GAMING", 5300),ASUS_RTX_3080("华硕 ASUS TUF GeForce RTX 3080-O10G-V2-GAMING", 6300),ASUS_RTX_4090("华硕 (ASUS) TUF-GeForce RTX 4090-O24G-GAMING", 14000),MSI_RTX_1660S("微星(MSI)魔龙 GeForce GTX 1660 SUPER", 2000),MSI_RTX_2060("微星(MSI)万图师 GeForce RTX 2060 VENTUS GP OC", 2000),MSI_RTX_3050("微星(MSI)万图师 GeForce RTX 3050 VENTUS 2X 8G OC", 2100),MSI_RTX_3060("微星(MSI)万图师 GeForce RTX 3060 VENTUS 2X 12G OC", 3000),MSI_RTX_3060_TI("微星(MSI)万图师 GeForce RTX 3060 Ti VENTUS 2X 8G OCV1 LHR", 3500),MSI_RTX_3090_TI("微星(MSI)超龙 GeForce RTX 3090 Ti SUPRIM X 24G", 13000),MSI_RTX_4090("微星(MSI)魔龙 GeForce RTX 4090 GAMING X TRIO 24G", 13000);private String type;private int price;GpuEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
MainBoardEnum.java
public enum MainBoardEnum implements CompunentEnum {ASUS_TUF_GAMING_B450M_PLUS("华硕TUF GAMING B450M-PLUS WIFI", 540),ASUS_TUF_GAMING_B550M_PLUS("华硕TUF GAMING B550M-PLUS WIFI", 850),ASUS_TUF_GAMING_B650M_PLUS("华硕TUF GAMING B650M-PLUS WIFI", 1500),ASUS_TUF_GAMING_B650_PLUS("华硕TUF GAMING B650-PLUS WIFI", 1900),ASUS_TUF_GAMING_X670E_PLUS("华硕(ASUS)TUF GAMING X670E-PLUS", 2600),ASUS_PRO_ART_X670E("华硕(ASUS)ProArt X670E-CREATOR WIFI", 5000),ASUS_TUF_GAMING_ZX670P_PLUS("华硕(ASUS)TUF GAMING ZX670-P", 2300),ASUS_PRIME_X670E_PRO("华硕(ASUS)PRIME X670E-PRO", 2600),COLORFUL_X570M_FROZEN("七彩虹(Colorful)CVN X570M GAMING FROZEN V14", 800),COLORFUL_X570M_PRO("七彩虹(Colorful)CVN X570M GAMING PRO V14", 750),COLORFUL_B550_FROZEN("七彩虹(Colorful)CVN B550M GAMING FROZEN V14", 630),COLORFUL_B550_PRO("七彩虹(Colorful)CVN B550M GAMING FROZEN V14", 650),COLORFUL_A520M_PRO("七彩虹(Colorful)A520M-K PRO V14", 400),GIGABYTE_B660_MASTER("技嘉(GIGABYTE)超级雕B660 AORUS MASTER", 1800),GIGABYTE_B660_PRO("技嘉(GIGABYTE)超级雕B660 AORUS MASTER", 1250);private String type;private int price;MainBoardEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
PowerEnum.java
public enum PowerEnum implements CompunentEnum {BUBALUS_200W("大水牛(BUBALUS)额定200W", 90),BUBALUS_250W("大水牛(BUBALUS)额定250W", 100),BUBALUS_300W("大水牛(BUBALUS)额定300W", 115),BUBALUS_400W("大水牛(BUBALUS)额定400W", 145),BUBALUS_500W("大水牛(BUBALUS)额定500W", 170),BUBALUS_600W("大水牛(BUBALUS)额定600W", 210),BUBALUS_650W("大水牛(BUBALUS)额定650W", 230),GIGABYTE_450W("技嘉(GIGABYTE)台式电脑主机电源 450W", 290),GIGABYTE_550W("技嘉(GIGABYTE)台式电脑主机电源 550W", 350),GIGABYTE_650W("技嘉(GIGABYTE)台式电脑主机电源 650W", 470),GIGABYTE_750W("技嘉(GIGABYTE)台式电脑主机电源 750W", 700),GIGABYTE_850W("技嘉(GIGABYTE)台式电脑主机电源 850W", 800),GREATEWALL_HOPE_350W("长城(GreatWall)额定350W HOPE-4500DS电脑电源", 190),GREATEWALL_HOPE_400W("长城(GreatWall)额定350W HOPE-5000DS电脑电源", 200),GREATEWALL_HOPE_450W("长城(GreatWall)额定350W HOPE-5500DS电脑电源", 210),GREATEWALL_HOPE_500W("长城(GreatWall)额定350W HOPE-6000DS电脑电源", 250),GREATEWALL_HOPE_600W("长城(GreatWall)额定350W HOPE-7000DS电脑电源", 310),GREATEWALL_HOPE_700W("长城(GreatWall)额定350W HOPE-8000DS电脑电源", 370),USCORSAIR_RM750_750W("美商海盗船 (USCORSAIR) RM750 750W电源", 900),USCORSAIR_RM850_850W("美商海盗船 (USCORSAIR) RM850 850W电源", 1000),USCORSAIR_RM650X_650W("美商海盗船 (USCORSAIR) RM650x 650W电源", 850),USCORSAIR_RM750X_750W("美商海盗船 (USCORSAIR) RM750x 750W电源", 850),USCORSAIR_RM850X_850W("美商海盗船 (USCORSAIR) RM850x 850W电源", 950),USCORSAIR_RM1000X_1000W("美商海盗船 (USCORSAIR) RM850x 850W电源", 1000),USCORSAIR_HX1000_1000W("美商海盗船 (USCORSAIR) HX1000 1000W电源", 1600),USCORSAIR_HX1000I_1000W("美商海盗船 (USCORSAIR) HX1000i 1000W电源", 1800),USCORSAIR_HX1200_1200W("美商海盗船 (USCORSAIR) HX1200 1200W电源", 3450),USCORSAIR_HX1500I_1500W("美商海盗船 (USCORSAIR) HX1500i 1600W电源", 2800),USCORSAIR_AX1500I_1600W("美商海盗船 (USCORSAIR) AX1600i 1600W电源", 3450),CPU_I9_12900("i9 12900", 3900);private String type;private int price;PowerEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
RamEnum.java
public enum RamEnum implements CompunentEnum {KINGSTON_DDR4_8G("金士顿 (Kingston) FURY 8GB DDR4 3200 8G", 220),KINGSTON_DDR4_16G("金士顿 (Kingston) FURY 8GB DDR4 3200 16G", 320),KINGSTON_DDR4_32G("金士顿 (Kingston) FURY 8GB DDR4 3200 32G", 800),ADATA_DDR4_8G("威刚(ADATA) XPG 龙耀 D50 吹雪限定款 DDR4 3600 RGB 8G", 350),ADATA_DDR4_16G("威刚(ADATA) XPG 龙耀 D50 吹雪限定款 DDR4 3600 RGB 16G", 600),USCORSAIR_DDR_3200_8G("美商海盗船(USCORSAIR) 8GB DDR4 3200 8G", 210),USCORSAIR_DDR_3200_16G("美商海盗船(USCORSAIR) 16GB DDR4 3200 16G", 400),USCORSAIR_DDR_3200_32G("美商海盗船(USCORSAIR) 32GB DDR4 3200 32G", 790),USCORSAIR_DDR_3600_8G("美商海盗船(USCORSAIR) 8GB DDR4 3600 8G", 290),USCORSAIR_DDR_3600_16G("美商海盗船(USCORSAIR) 16GB DDR4 3600 16G", 520),USCORSAIR_DDR_3600_32G("美商海盗船(USCORSAIR) 32GB DDR4 3600 32G", 1320);private String type;private int price;RamEnum(String type, int price) {this.type = type;this.price = price;}@Overridepublic String getType() {return this.type;}@Overridepublic int getPrice() {return this.price;}
}
如何使用
到这里整个电脑组装基于装饰器设计模式的代码已经写完,为了方便大家看清,这里简单画个关系图,帮助大家去理解。
下面看如果使用
ComputerTest.java
public class ComputerTest {public static void main(String[] args) {Computer computer = new DiyComputer();// 七彩虹七彩虹主板computer = new MainBoardComponent(computer, MainBoardEnum.COLORFUL_A520M_PRO);// 技嘉450W电源computer = new PowerComponent(computer, PowerEnum.GIGABYTE_450W);// cpu i5 12代computer = new CpuComponent(computer, CpuEnum.CPU_I5_12600);// 显卡1660computer = new GpuComponent(computer, GpuEnum.ASUS_GTX_1660S);// 一个256BG的SSD,一个2T的机械computer = new DiskComponent(computer, DiskEnum.ST_SATA_2T);computer = new DiskComponent(computer, DiskEnum.SAMSUNG_SSD_870_EVO_256G);// 四个8Gcomputer = new RamComponent(computer, RamEnum.KINGSTON_DDR4_8G);computer = new RamComponent(computer, RamEnum.KINGSTON_DDR4_8G);computer = new RamComponent(computer, RamEnum.KINGSTON_DDR4_8G);computer = new RamComponent(computer, RamEnum.KINGSTON_DDR4_8G);System.out.println(computer.getDesc());System.out.println("价格: " + computer.getPrice());}}
下运行结果(小明同学花了6000块组装了一台可以吃鸡的电脑了)
同事小王发了年终,想给自己配一台高配台式机玩使命召唤19,大家觉得小王同学配的这台电脑怎么样
public class ComputerTest2 {public static void main(String[] args) {Computer computer = new RogComputer();// 华硕主板computer = new MainBoardComponent(computer, MainBoardEnum.ASUS_PRO_ART_X670E);// 海盗船1600W带能源computer = new PowerComponent(computer, PowerEnum.USCORSAIR_AX1500I_1600W);// cpu i9 12代computer = new CpuComponent(computer, CpuEnum.CPU_I9_12900);// 显卡4090computer = new GpuComponent(computer, GpuEnum.ASUS_RTX_4090);// 两个三星SSD 2Tcomputer = new DiskComponent(computer, DiskEnum.SAMSUNG_SSD_980_PRO_HEATSINT_2T);computer = new DiskComponent(computer, DiskEnum.SAMSUNG_SSD_980_PRO_HEATSINT_2T);// 四个海盗船32G内存computer = new RamComponent(computer, RamEnum.USCORSAIR_DDR_3600_32G);computer = new RamComponent(computer, RamEnum.USCORSAIR_DDR_3600_32G);computer = new RamComponent(computer, RamEnum.USCORSAIR_DDR_3600_32G);computer = new RamComponent(computer, RamEnum.USCORSAIR_DDR_3600_32G);System.out.println(computer.getDesc());System.out.println("价格: " + computer.getPrice());}}
最后,有不对的地方欢迎大家指正,时间比较仓促,例子还有很多缺陷需要改正的地方~
第一个:ComputerTest中小明如果没有买电源,其实这个时候是无法组装电脑,由于电脑的特殊性,并不是完全适合修饰器设计模式的,还需要我们加上完备性校验,哪些配件是必须品哪些是可选配件,哪些配件可以购买多个,还有主板支不支持等等,这些都可以去完善。
第二个:针对上面问题可以结合工厂设计模式,所以对于电脑小白不由他自己去选购配件,而是直接提供诉求,由电脑工厂去直接去配置出一台完整的电脑给消费者。