提交 8f25c368 编写于 作者: ツぃ☆ve芜情's avatar ツぃ☆ve芜情

Initial commit

上级
设计模式是系统服务设计中针对常见场景的一种解决方案,可以解决功能逻辑开发中遇到的共性问题。
设计模式是一种开发指导思想,不要拘泥于某种已经存在的固定代码格式,而要根据实际的业务场景做出改变。
按照实现形式,设计模式可以分为三类:
- **创建型模式**
提供创建对象的机制,提升已有代码的灵活性和可复用性
- **结构型模式**
介绍如何将对象和类组装成较⼤大的结构, 并同时保持结构的灵活和⾼高效
- **行为模式**
负责对象间的高效沟通和职责传递委派
设计模式有六大设计原则:
1. **单一职责原则**
它规定一个类应该只有一个发生变化的原因
2. **开闭原则**
开闭原则规定软件对象中的对象、类、模块和函数对扩展应该是开放的,但是对于修改是封闭的。这意味着应该用抽象定义结构,用具体实现扩展细节,核心思想可以理解为面向抽象编程。
3. **里氏替换原则**
如果 S 是 T 的子类型,那么所有 T 类型的对象都可以在不破坏程序的情况下被 S 类型的对象替换。
简单来说,子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:当子类继承父类时,除添加新的方法且完成新的功能外,尽量不要重写父类的方法。这句话包括了四点含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 子类可以增加自己特有的方法
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
- 当子类的方法实现父类的方法(重写、重载或实现抽象方法)时,方法的后置条件(即方法的输出或返回值)要比父类的方法更严格或与父类的方法相等
里氏替换原则的作用:
- 里氏替换原则是实现开闭原则的重要方式之一
- 解决了继承中重写父类造成的可复用性变差的问题
- 是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性
- 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险
4. **迪米特法则原则**
迪米特法则又称为最小知道原则,是指一个对象类对于其他对象类来说,知道的越少越好。也就是说,两个类直接不要有过多的耦合关系,保持最少关联性。
5. **接口隔离原则**
一个类对另一个类的依赖应该建立在最小的接口上
接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
再具体应用接口隔离原则时,应根据以下几个规则衡量:
- 接口尽量小,但是要有限度。一个接口只服务与一个子模块或业务逻辑。
- 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
- 了解环境,拒绝盲从。
- 提高内聚,减少对外交互。
6. **依赖倒置原则**
依赖倒置原则是指在设计代码架构时,高层模块不应该依赖于底层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
依赖倒置原则是实现开闭原则的重要途径之一,它降低了类之间的耦合,提高了系统的稳定性和可维护性。
<svg id="SvgjsSvg1176" width="594" height="310" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"><defs id="SvgjsDefs1177"><marker id="SvgjsMarker1230" markerWidth="14" markerHeight="12" refX="16" refY="6" viewBox="0 0 14 12" orient="auto" markerUnits="userSpaceOnUse" stroke-dasharray="0,0"><path id="SvgjsPath1231" d="M1,1 L14,6 L1,11L1,1" fill="#ffffff" stroke="#323232" stroke-width="2"></path></marker><marker id="SvgjsMarker1234" markerWidth="14" markerHeight="12" refX="16" refY="6" viewBox="0 0 14 12" orient="auto" markerUnits="userSpaceOnUse" stroke-dasharray="0,0"><path id="SvgjsPath1235" d="M1,1 L14,6 L1,11L1,1" fill="#ffffff" stroke="#323232" stroke-width="2"></path></marker></defs><g id="SvgjsG1178" transform="translate(197,25)"><path id="SvgjsPath1179" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 96Q 200 100 196 100L 4 100Q 0 100 0 96Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1180" d="M 0 30L 200 30M 0 60L 200 60" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1181" d="M 0 0L 200 0L 200 100L 0 100Z" stroke="none" fill="none"></path><g id="SvgjsG1182"><text id="SvgjsText1183" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="5.375" transform="rotate(0)"><tspan id="SvgjsTspan1184" dy="16" x="100"><tspan id="SvgjsTspan1185" style="text-decoration:;">Logistics</tspan></tspan></text></g><g id="SvgjsG1186"><text id="SvgjsText1187" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="35.375" transform="rotate(0)"><tspan id="SvgjsTspan1188" dy="16" x="10"><tspan id="SvgjsTspan1189" style="text-decoration:;"># transport Transport</tspan></tspan></text></g><g id="SvgjsG1190"><text id="SvgjsText1191" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="58.375" transform="rotate(0)"><tspan id="SvgjsTspan1192" dy="16" x="10"><tspan id="SvgjsTspan1193" style="text-decoration:;">- createTransport():void</tspan></tspan><tspan id="SvgjsTspan1194" dy="16" x="10"><tspan id="SvgjsTspan1195" style="text-decoration:;">+ playDelivery():void</tspan></tspan></text></g></g><g id="SvgjsG1196" transform="translate(25,195)"><path id="SvgjsPath1197" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 86Q 200 90 196 90L 4 90Q 0 90 0 86Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1198" d="M 0 30L 200 30M 0 60L 200 60" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1199" d="M 0 0L 200 0L 200 90L 0 90Z" stroke="none" fill="none"></path><g id="SvgjsG1200"><text id="SvgjsText1201" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="5.375" transform="rotate(0)"><tspan id="SvgjsTspan1202" dy="16" x="100"><tspan id="SvgjsTspan1203" style="text-decoration:;">RoadLogistics</tspan></tspan></text></g><g id="SvgjsG1204"><text id="SvgjsText1205" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="35.375" transform="rotate(0)"><tspan id="SvgjsTspan1206" dy="16" x="10"><tspan id="SvgjsTspan1207" style="text-decoration:;">...</tspan></tspan></text></g><g id="SvgjsG1208"><text id="SvgjsText1209" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="65.375" transform="rotate(0)"><tspan id="SvgjsTspan1210" dy="16" x="10"><tspan id="SvgjsTspan1211" style="text-decoration:;">- createTransport():void</tspan></tspan></text></g></g><g id="SvgjsG1212" transform="translate(369,195)"><path id="SvgjsPath1213" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 86Q 200 90 196 90L 4 90Q 0 90 0 86Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1214" d="M 0 30L 200 30M 0 60L 200 60" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1215" d="M 0 0L 200 0L 200 90L 0 90Z" stroke="none" fill="none"></path><g id="SvgjsG1216"><text id="SvgjsText1217" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="5.375" transform="rotate(0)"><tspan id="SvgjsTspan1218" dy="16" x="100"><tspan id="SvgjsTspan1219" style="text-decoration:;">SeaLogistics</tspan></tspan></text></g><g id="SvgjsG1220"><text id="SvgjsText1221" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="35.375" transform="rotate(0)"><tspan id="SvgjsTspan1222" dy="16" x="10"><tspan id="SvgjsTspan1223" style="text-decoration:;">...</tspan></tspan></text></g><g id="SvgjsG1224"><text id="SvgjsText1225" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="65.375" transform="rotate(0)"><tspan id="SvgjsTspan1226" dy="16" x="10"><tspan id="SvgjsTspan1227" style="text-decoration:;">- createTransport():void</tspan></tspan></text></g></g><g id="SvgjsG1228"><path id="SvgjsPath1229" d="M125 195L125 160L297 160L297 125" stroke="#323232" stroke-width="2" fill="none" marker-end="url(#SvgjsMarker1230)"></path></g><g id="SvgjsG1232"><path id="SvgjsPath1233" d="M469 195L469 160L297 160L297 125" stroke="#323232" stroke-width="2" fill="none" marker-end="url(#SvgjsMarker1234)"></path></g></svg>
\ No newline at end of file
<svg id="SvgjsSvg1006" width="545.390625" height="258.78125" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"><defs id="SvgjsDefs1007"><marker id="SvgjsMarker1056" markerWidth="14" markerHeight="12" refX="16" refY="6" viewBox="0 0 14 12" orient="auto" markerUnits="userSpaceOnUse" stroke-dasharray="0,0"><path id="SvgjsPath1057" d="M1,1 L14,6 L1,11L1,1" fill="#ffffff" stroke="#323232" stroke-width="2"></path></marker><marker id="SvgjsMarker1060" markerWidth="14" markerHeight="12" refX="16" refY="6" viewBox="0 0 14 12" orient="auto" markerUnits="userSpaceOnUse" stroke-dasharray="0,0"><path id="SvgjsPath1061" d="M1,1 L14,6 L1,11L1,1" fill="#ffffff" stroke="#323232" stroke-width="2"></path></marker></defs><g id="SvgjsG1008" transform="translate(172.7055288461537,25.012019230769113)"><path id="SvgjsPath1009" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 56Q 200 60 196 60L 4 60Q 0 60 0 56Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1010" d="M 0 32L 200 32" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1011" d="M 0 0L 200 0L 200 60L 0 60Z" stroke="none" fill="none"></path><g id="SvgjsG1012"><text id="SvgjsText1013" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="-1.625" transform="rotate(0)"><tspan id="SvgjsTspan1014" dy="16" x="100"><tspan id="SvgjsTspan1015" style="text-decoration:;">&lt;&lt;interface&gt;&gt;</tspan></tspan><tspan id="SvgjsTspan1016" dy="16" x="100"><tspan id="SvgjsTspan1017" style="text-decoration:;">Transport</tspan></tspan></text></g><g id="SvgjsG1018"><text id="SvgjsText1019" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="36.375" transform="rotate(0)"><tspan id="SvgjsTspan1020" dy="16" x="10"><tspan id="SvgjsTspan1021" style="text-decoration:;">+ deliver() : void</tspan></tspan></text></g></g><g id="SvgjsG1022" transform="translate(25.01322115384619,143.78125)"><path id="SvgjsPath1023" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 86Q 200 90 196 90L 4 90Q 0 90 0 86Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1024" d="M 0 30L 200 30M 0 60L 200 60" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1025" d="M 0 0L 200 0L 200 90L 0 90Z" stroke="none" fill="none"></path><g id="SvgjsG1026"><text id="SvgjsText1027" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="5.375" transform="rotate(0)"><tspan id="SvgjsTspan1028" dy="16" x="100"><tspan id="SvgjsTspan1029" style="text-decoration:;">Truck</tspan></tspan></text></g><g id="SvgjsG1030"><text id="SvgjsText1031" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="35.375" transform="rotate(0)"><tspan id="SvgjsTspan1032" dy="16" x="10"><tspan id="SvgjsTspan1033" style="text-decoration:;">...</tspan></tspan></text></g><g id="SvgjsG1034"><text id="SvgjsText1035" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="65.375" transform="rotate(0)"><tspan id="SvgjsTspan1036" dy="16" x="10"><tspan id="SvgjsTspan1037" style="text-decoration:;">+ deliver() : void</tspan></tspan></text></g></g><g id="SvgjsG1038" transform="translate(320.3978365384612,143.78125)"><path id="SvgjsPath1039" d="M 0 4Q 0 0 4 0L 196 0Q 200 0 200 4L 200 86Q 200 90 196 90L 4 90Q 0 90 0 86Z" stroke="rgba(50,50,50,1)" stroke-width="2" fill-opacity="1" fill="#ffffff"></path><path id="SvgjsPath1040" d="M 0 30L 200 30M 0 60L 200 60" stroke="rgba(50,50,50,1)" stroke-width="2" fill="none"></path><path id="SvgjsPath1041" d="M 0 0L 200 0L 200 90L 0 90Z" stroke="none" fill="none"></path><g id="SvgjsG1042"><text id="SvgjsText1043" font-family="微软雅黑" text-anchor="middle" font-size="13px" width="180px" fill="#323232" font-weight="700" align="middle" lineHeight="125%" anchor="middle" family="微软雅黑" size="13px" weight="700" font-style="" opacity="1" y="5.375" transform="rotate(0)"><tspan id="SvgjsTspan1044" dy="16" x="100"><tspan id="SvgjsTspan1045" style="text-decoration:;">Ship</tspan></tspan></text></g><g id="SvgjsG1046"><text id="SvgjsText1047" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="35.375" transform="rotate(0)"><tspan id="SvgjsTspan1048" dy="16" x="10"><tspan id="SvgjsTspan1049" style="text-decoration:;">...</tspan></tspan></text></g><g id="SvgjsG1050"><text id="SvgjsText1051" font-family="微软雅黑" text-anchor="start" font-size="13px" width="180px" fill="#323232" font-weight="400" align="middle" lineHeight="125%" anchor="start" family="微软雅黑" size="13px" weight="400" font-style="" opacity="1" y="65.375" transform="rotate(0)"><tspan id="SvgjsTspan1052" dy="16" x="10"><tspan id="SvgjsTspan1053" style="text-decoration:;">+ deliver() : void</tspan></tspan></text></g></g><g id="SvgjsG1054"><path id="SvgjsPath1055" d="M125.01322115384619 143.78125L125.01322115384619 114.39663461538453L272.7055288461537 114.39663461538453L272.7055288461537 85.01201923076911" stroke-dasharray="8,5" stroke="#323232" stroke-width="2" fill="none" marker-end="url(#SvgjsMarker1056)"></path></g><g id="SvgjsG1058"><path id="SvgjsPath1059" d="M420.3978365384612 143.78125L420.3978365384612 114.39663461538453L272.7055288461537 114.39663461538453L272.7055288461537 85.01201923076911" stroke-dasharray="8,5" stroke="#323232" stroke-width="2" fill="none" marker-end="url(#SvgjsMarker1060)"></path></g></svg>
\ No newline at end of file
工厂模式也称简单工厂模式,是创建型设计模式的一种,这种设计模式提供了按需创建对象的最佳方式。同时,这种创建方式不会对外暴露创建细节,并且会通过一个统一的接口创建所需对象。
## 特点
1. **定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类**
2. 在简单工厂模式中用于被创建实例的方法通常为**静态(static)方法**,因此简单工厂模式又被成为**静态工厂方法(Static Factory Method)**
3. 需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程
4. 例如,我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会为其提供所需要的披萨(当然是要钱的哈哈),如果其所需的我这没有,则是另外的情况,后面会谈。这时候,我这家**披萨店就可以看做工厂(Factory)**,而生产出来的**披萨被成为产品(Product)****披萨的名称则被称为参**数,工厂可以根据参数的不同返回不同的产品,这就是**简单工厂模式**
## 示例
假设你正在开发一款物流管理应用。 最初版本只能处理卡车运输, 因此大部分代码都在位于名为 `卡车` 的类中。一段时间后, 这款应用变得极受欢迎。 你每天都能收到十几次来自海运公司的请求, 希望应用能够支持海上物流功能。
![](img/problem1-zh.png)
这可是个好消息。 但是代码问题该如何处理呢? 目前, 大部分代码都与 `卡车` 类相关。 在程序中添加 `轮船` 类需要修改全部代码。 更糟糕的是, 如果你以后需要在程序中支持另外一种运输方式, 很可能需要再次对这些代码进行大幅修改。
最后, 你将不得不编写繁复的代码, 根据不同的运输对象类, 在应用中进行不同的处理。
## 解决方案
![](img/Transport.svg)
【运输工具】
```java
public interface Transport {
void deliver();
}
```
【货车运输】
```java
public class Truck implements Transport {
@Override
public void deliver() {
System.out.println("货车运输中...");
}
}
```
【轮船运输】
```java
public class Ship implements Transport {
@Override
public void deliver() {
System.out.println("轮船运输中...");
}
}
```
![](img/Logistics.svg)
【物流】
```java
public abstract class Logistics {
protected Transport transport;
protected abstract Transport createTransport();
public void playDelivery(){
System.out.println("物流开始....");
transport.deliver();
System.out.println("物流结束....");
}
}
```
【陆路运输】
```java
public class RoadLogistics extends Logistics{
public RoadLogistics() {
super();
this.transport = createTransport();
}
@Override
protected Transport createTransport() {
return new Truck();
}
}
```
【水路运输】
```java
public class SeaLogistics extends Logistics{
public SeaLogistics() {
super();
this.transport = createTransport();
}
@Override
protected Transport createTransport() {
return new Ship();
}
}
```
![](img/factory-method-mini.png)
【测试类】
```java
public class Test {
public static void main(String[] args) {
System.out.println("======第一次物流======");
Logistics seaLogistics = new SeaLogistics();
seaLogistics.playDelivery();
System.out.println("======第二次物流======");
Logistics roadLogistics = new RoadLogistics();
roadLogistics.playDelivery();
}
}
```
输出结果:
```
======第一次物流======
物流开始....
轮船运输中...
物流结束....
======第二次物流======
物流开始....
货车运输中...
物流结束....
```
**抽象工厂模式**是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
![](img/abstract-factory-zh.png)
## 问题
假设你正在开发一款家具商店模拟器。 你的代码中包括一些类, 用于表示:
1. 一系列相关产品, 例如 `椅子` Chair 、 `沙发` Sofa 和 `咖啡桌` Coffee­Table 。
2. 系列产品的不同变体。 例如, 你可以使用 `现代` Modern 、 `维多利亚` Victorian 、 `装饰风艺术` Art­Deco 等风格生成 `椅子``沙发``咖啡桌`
![](img/problem-zh.png)
<center>系列产品及其不同变体</center>
你需要设法单独生成每件家具对象, 这样才能确保其风格一致。 如果顾客收到的家具风格不一样, 他们可不会开心。
![](img/abstract-factory-comic-1-zh.png)
<center>现代风格的沙发和维多利亚风格的椅子不搭。</center>
此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁, 你不会想在每次更新时都去修改核心代码的。
## 解决方案
每一个模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。
在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族和产品等级。
所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如 AMD 的主板、芯片组、CPU 组成一个家族,Intel的主板、芯片组、CPU 组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:
![](img/产品族.png)
显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:
![](img/工厂模式缺点.png)
那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。
![](img/抽象工厂.png)
下面回到家具的案例,我们有一个家具工厂,包含不同的生产线,每个生产线生产不同风格的产品,这样,客户购买时,选择风格后,会调用对应生产线生产对应风格的家具。
![](img/抽象工厂模式.png)
## 代码实现
1. 创建家具产品类及其子类
【Chair】
```java
public abstract class Chair{
public abstract void info();
}
```
【ModernChair】
```java
public class ModernChair extends Chair{
@Override
public void info() {
System.out.println("这是现代风格的椅子");
}
}
```
【VictorianChair】
```java
public class VictorianChair extends Chair{
@Override
public void info() {
System.out.println("这是维多利亚风格的椅子");
}
}
```
【ArtDecoChair】
```java
public class ArtDecoChair extends Chair{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的椅子");
}
}
```
【Sofa】
```java
public abstract class Sofa {
public abstract void info();
}
```
【ModernSofa】
```java
public class ModernSofa extends Sofa{
@Override
public void info() {
System.out.println("这是现代风格的沙发");
}
}
```
【VictorianSofa】
```java
public class VictorianSofa extends Sofa{
@Override
public void info() {
System.out.println("这是维多利亚风格的沙发");
}
}
```
【ArtDecoSofa】
```java
public class ArtDecoSofa extends Sofa{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的沙发");
}
}
```
【CoffeeTable】
```java
public abstract class CoffeeTable{
public abstract void info();
}
```
【ModernCoffeeTable】
```java
public class ModernCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是现代风格的咖啡桌");
}
}
```
【VictorianCoffeeTable】
```java
public class VictorianCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是维多利亚风格的咖啡桌");
}
}
```
【ArtDecoCoffeeTable】
```java
public class ArtDecoCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的咖啡桌");
}
}
```
2. 定义风格枚举类
【FurnitureStyle】
```java
public enum FurnitureStyle {
Modern,
Victorian,
ArtDeco
}
```
3. 创建家具工厂类(抽象工厂)
【FurnitureFactory】
```java
public interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
CoffeeTable createCoffeeTable();
}
```
4. 创建各种风格生产线(具体工厂)
【ModernLine】
```java
public class ModernLine implements FurnitureFactory{
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Sofa createSofa() {
return new ModernSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new ModernCoffeeTable();
}
}
```
【VictorianLine】
```java
public class VictorianLine implements FurnitureFactory {
@Override
public Chair createChair() {
return new VictorianChair();
}
@Override
public Sofa createSofa() {
return new VictorianSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new VictorianCoffeeTable();
}
}
```
【ArtDecoLine】
```java
public class ArtDecoLine implements FurnitureFactory {
@Override
public Chair createChair() {
return new ArtDecoChair();
}
@Override
public Sofa createSofa() {
return new ArtDecoSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new ArtDecoCoffeeTable();
}
}
```
5. 创建客户(调用者)
【Client】
```java
public class Client {
private FurnitureFactory factory;
public Client(FurnitureStyle style) {
if (style == FurnitureStyle.Modern) {
this.factory = new ModernLine();
} else if (style == FurnitureStyle.Victorian) {
this.factory = new VictorianLine();
} else if (style == FurnitureStyle.ArtDeco) {
this.factory = new ArtDecoLine();
} else {
throw new NoSuchElementException("没有此种风格生产线");
}
}
public void purchaseFurniture() {
Chair chair = factory.createChair();
Sofa sofa = factory.createSofa();
CoffeeTable coffeeTable = factory.createCoffeeTable();
chair.info();
sofa.info();
coffeeTable.info();
}
}
```
6. 测试
```java
public class Test {
public static void main(String[] args) {
Client client = new Client(FurnitureStyle.Victorian);
client.purchaseFurniture();
}
}
```
输出结果:
```
这是装饰风艺术风格的椅子
这是装饰风艺术风格的沙发
这是装饰风艺术风格的咖啡桌
```
## 在什么情况下应当使用抽象工厂模式
1. 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的
2. 这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品
3. 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来
4. 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现
## 抽象工厂模式的优点
- **分离接口和实现**
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦
- **使切换产品族变得容易**
因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂
## 抽象工厂模式的缺点
- **不太容易扩展新的产品**
如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类
建造者模式实际上是常用的设计模式。顾名思义,builder 的意思是建造者或者建筑工人,谈到建造自然会想到楼房。楼房是千差万别的,楼房的外形、层数、内部房间的数量、房间的装饰等等都不一样,但是对于建造者来说,抽象出来的建筑流程是确定的,往往建筑一座楼房包括下面的步骤:(1)打桩,建立基础(2)建立框架等。
建造者模式的本质和建造楼房是一致的:即流程不变,但每个流程实现的具体细节则是经常变化的。建造者模式的好处就是保证了流程不会变化,流程即不会增加、也不会遗漏或者产生流程次序错误,这是非常重要的。我们熟知的楼歪歪事件,官方的解释就是由于先建立楼房后,再建设停车场造成的,这是典型的建造次序错乱。
**建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。**
建造者模式通常包括下面几个角色:
- Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
- ConcreteBuilder:实现 Builder 接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
- Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
- Product:要创建的复杂对象(不是必须的,有些情况仅仅是流程)。
![](img/Builder.png)
## 示例 1
为了身体健康,我们每个人都断练过,而学生有男有女,由于身体差异,能做的动作也是迥然不同,但是都是有着先后顺序,假设我们锻炼都要做三组动作,而男女生的动作并不相同,我们在实现这个过程时可以让男女生分别实现锻炼这个接口,在自己的类中实现不同的锻炼动作。
【Builder】
```java
public interface Exercise {
Exercise first();
Exercise second();
Exercise third();
}
```
【ConcreteBuilder 】
```java
public class Boy implements Exercise{
@Override
public Exercise first() {
System.out.println("卧推");
return this;
}
@Override
public Exercise second() {
System.out.println("高位下拉");
return this;
}
@Override
public Exercise third() {
System.out.println("硬拉");
return this;
}
}
```
```java
public class Girl implements Exercise {
@Override
public Exercise first() {
System.out.println("跑步机慢跑");
return this;
}
@Override
public Exercise second() {
System.out.println("引体向上");
return this;
}
@Override
public Exercise third() {
System.out.println("仰卧起坐");
return this;
}
}
```
【Director】
```java
public class Student {
private Exercise exercise;
public Student(Exercise exercise) {
this.exercise = exercise;
}
public void doExercise() {
exercise.first().second().third();
}
}
```
【测试类】
```java
public class Test {
public static void main(String[] args) {
Student girl = new Student(new Girl());
girl.doExercise();
System.out.println("===========================");
Student boy = new Student(new Boy());
boy.doExercise();
}
}
```
输出结果:
```
跑步机慢跑
引体向上
仰卧起坐
===========================
卧推
高位下拉
硬拉
```
## 示例 2
下面我们看一个 KFC 点套餐的例子,我们点餐可以点一个汉堡和一个冷饮,汉堡可以是鸡肉汉堡、虾堡等等,是装在盒子中的,冷饮可以是可乐、雪碧等等,是装在瓶子中的。下面我们来用建造者模式对其进行组合,用户只需提交订单即可,UML 图如下:
![](img/KFC.png)
【Item 接口】
创建一个表示食物条目和食物包装的接口。
```java
public interface Item {
//获取食物名称
public String getName();
//获取包装
public Packing packing();
//获取价格
public float getPrice();
}
```
【可包装接口及其实现类】
Packable 接口的实现类。Wrapper 为纸盒包装,Bottle为瓶装。
```java
public interface Packable {
//获取包装类型
public String getPack();
}
```
```java
public class Wrapper implements Packable {
@Override
public String getPack() {
return "纸盒";
}
}
```
```java
public class Bottle implements Packable {
@Override
public String getPack() {
return "纸杯";
}
}
```
【食品类】
创建实现 Item 接口的抽象类。Burger 为汉堡,Drink 为饮品。
```java
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float getPrice();
}
```
```java
public abstract class Drink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float getPrice();
}
```
【具体食品类】
创建扩展了 Burger 和 Drink 的具体实现类。这里简单的就设为 Burger1、Burger2、Drink1、Drink2。各写一个,多余的就不赘述了。
```java
public class Burger1 extends Burger {
@Override
public String getName() {
return "汉堡1";
}
@Override
public float getPrice() {
return 25.0f;
}
}
```
```java
public class Drink1 extends Drink {
@Override
public String getName() {
return "饮品1";
}
@Override
public float getPrice() {
return 15.0f;
}
}
```
【Meal 类】
```java
public class Meal {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
//获取总消费
public float getCost() {
float cost = 0.0f;
for (Item item : items) {
cost += item.getPrice();
}
return cost;
}
public void showItem() {
for (Item item : items) {
System.out.print("餐品:" + item.getName());
System.out.print(",包装:" + item.packing().getPack());
System.out.println(",价格:¥" + item.getPrice());
}
}
}
```
【套餐类】
```java
public class Package {
//套餐 1
public Meal package1() {
Meal meal = new Meal();
meal.addItem(new Burger1());
meal.addItem(new Drink1());
return meal;
}
//套餐 2
public Meal package2() {
Meal meal = new Meal();
meal.addItem(new Burger2());
meal.addItem(new Drink2());
return meal;
}
}
```
【消费者】
```java
public class Consumer {
public static void main(String[] args) {
Package set = new Package();
// 购买第一个套餐
Meal order1 = set.package1();
System.out.println("------order1------");
order1.showItem();
System.out.println("总额:¥" + order1.getCost());
// 购买第二个套餐
Meal order2 = set.package2();
System.out.println("------order2------");
order2.showItem();
System.out.println("总额:¥" + order2.getCost());
}
}
```
输出结果:
```java
------order1------
餐品汉堡1包装纸盒价格:¥25.0
餐品饮品1包装纸杯价格:¥15.0
总额:¥40.0
------order2------
餐品汉堡2包装纸盒价格:¥35.0
餐品饮品2包装纸杯价格:¥18.0
总额:¥53.0
```
> 这部分示例参见小傅哥的 [重学Java设计模式——原型模式](https://bugstack.cn/md/develop/design-pattern/2020-05-28-%E9%87%8D%E5%AD%A6%20Java%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E5%AE%9E%E6%88%98%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%8B.html)
**定义:**用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
![](img/prototype.png)
## 案例
每个人都经历过考试,从纸制版到上机答题,大大小小也有几百场。而以前坐在教室里答题身边的人都是一套试卷,考试的时候还能偷摸或者别人给发信息抄一抄答案。
但从一部分可以上机考试的内容开始,在保证大家的公平性一样的题目下,开始出现试题混排更有做的好的答案选项也混排。这样大大的增加了抄的成本,也更好的做到了考试的公平性。
**但如果这个公平性的考试需求交给你来完成,你会怎么做?**
因为需要实现一个上机考试抽题的服务,因此在这里建造一个题库题目的场景类信息,用于创建;`选择题``问答题`
【选择题】
```java
public class ChoiceQuestion {
private String name;
private Map<String, String> option;
private String key;
public ChoiceQuestion(String name, Map<String, String> option, String key) {
this.name = name;
this.option = option;
this.key = key;
}
public ChoiceQuestion() {
}
// getter and setter
}
```
【问答题】
```java
public class AnswerQuestion {
private String name; // 问题
private String key; // 答案
public AnswerQuestion(String name, String key) {
this.name = name;
this.key = key;
}
public AnswerQuestion() {
}
// getter and setter
}
```
## 实现
原型模式主要解决的问题就是创建大量重复的类,而我们模拟的场景就需要给不同的用户都创建相同的试卷,但这些试卷的题目不便于每次都从库中获取,甚至有时候需要从远程的 RPC 中获取。这样都是非常耗时的,而且随着创建对象的增多将严重影响效率。
在原型模式中所需要的非常重要的手段就是克隆,在需要用到克隆的类中都需要实现 `implements Cloneable` 接口。
【题目选项乱序操作工具包】
``` java
public class TopicRandomUtil {
/**
* 乱序Map元素,记录对应答案key
*
* @param option 题目
* @param key 答案
* @return Topic 乱序后 {A=c., B=d., C=a., D=b.}
*/
public static Topic random(Map<String, String> option, String key) {
Set<String> keySet = option.keySet();
List<String> keyList = new ArrayList<>(keySet);
Collections.shuffle(keyList);
Map<String, String> optionNew = new HashMap<>();
int idx = 0;
String keyNew = "";
for (String next : keySet) {
String randomKey = keyList.get(idx++);
if (key.equals(next)) {
keyNew = randomKey;
}
optionNew.put(randomKey, option.get(next));
}
return new Topic(optionNew, keyNew);
}
}
```
【乱序后的选项】
```java
public class Topic {
private Map<String, String> option; // 选项;A、B、C、D
private String key; // 答案;B
public Topic() {
}
public Topic(Map<String, String> option, String key) {
this.option = option;
this.key = key;
}
public Map<String, String> getOption() {
return option;
}
public void setOption(Map<String, String> option) {
this.option = option;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
```
【克隆对象处理类】
```java
public class QuestionBank implements Cloneable{
private String candidate; // 考生
private String number; // 考号
private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<ChoiceQuestion>();
private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<AnswerQuestion>();
public QuestionBank append(ChoiceQuestion choiceQuestion) {
choiceQuestionList.add(choiceQuestion);
return this;
}
public QuestionBank append(AnswerQuestion answerQuestion) {
answerQuestionList.add(answerQuestion);
return this;
}
@Override
public Object clone() throws CloneNotSupportedException {
QuestionBank questionBank = (QuestionBank) super.clone();
questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();
// 题目乱序
Collections.shuffle(questionBank.choiceQuestionList);
Collections.shuffle(questionBank.answerQuestionList);
// 答案乱序
ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
for (ChoiceQuestion question : choiceQuestionList) {
Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());
question.setOption(random.getOption());
question.setKey(random.getKey());
}
return questionBank;
}
public void setCandidate(String candidate) {
this.candidate = candidate;
}
public void setNumber(String number) {
this.number = number;
}
@Override
public String toString() {
StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
"考号:" + number + "\r\n" +
"--------------------------------------------\r\n" +
"一、选择题" + "\r\n\n");
for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
Map<String, String> option = choiceQuestionList.get(idx).getOption();
for (String key : option.keySet()) {
detail.append(key).append(":").append(option.get(key)).append("\r\n");;
}
detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
}
detail.append("二、问答题" + "\r\n\n");
for (int idx = 0; idx < answerQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(answerQuestionList.get(idx).getName()).append("\r\n");
detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
}
return detail.toString();
}
}
```
这里的主要操作内容有三个,分别是:
- 两个 `append()`,对各项题目的添加
- `clone()`,这里的核心操作就是对对象的复制,这里的复制不只是包括了本身,同时对两个集合也做了复制。只有这样的拷贝才能确保在操作克隆对象的时候不影响原对象
- 乱序操作,在 `list` 集合中有一个方法,`Collections.shuffle`,可以将原有集合的顺序打乱,输出一个新的顺序。在这里我们使用此方法对题目进行乱序操作
> <font size=5 color="blue"><b>PS:深拷贝与浅拷贝问题中,会发生深拷贝的有 java 中的 8 种基本类型以及他们的封装类型,另外还有 String 类型,其余的都是浅拷贝。</b></font>
【初始化试卷数据】
```java
public class QuestionBankController {
private QuestionBank questionBank = new QuestionBank();
public QuestionBankController() {
Map<String, String> map01 = new HashMap<>();
map01.put("A", "JAVA2 EE");
map01.put("B", "JAVA2 Card");
map01.put("C", "JAVA2 ME");
map01.put("D", "JAVA2 HE");
map01.put("E", "JAVA2 SE");
Map<String, String> map02 = new HashMap<>();
map02.put("A", "JAVA程序的main方法必须写在类里面");
map02.put("B", "JAVA程序中可以有多个main方法");
map02.put("C", "JAVA程序中类名必须与文件名一样");
map02.put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");
Map<String, String> map03 = new HashMap<>();
map03.put("A", "变量由字母、下划线、数字、$符号随意组成;");
map03.put("B", "变量不能以数字作为开头;");
map03.put("C", "A和a在java中是同一个变量;");
map03.put("D", "不同类型的变量,可以起相同的名字;");
Map<String, String> map04 = new HashMap<>();
map04.put("A", "STRING");
map04.put("B", "x3x;");
map04.put("C", "void");
map04.put("D", "de$f");
Map<String, String> map05 = new HashMap<>();
map05.put("A", "31");
map05.put("B", "0");
map05.put("C", "1");
map05.put("D", "2");
questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", map01, "D"))
.append(new ChoiceQuestion("下列说法正确的是", map02, "A"))
.append(new ChoiceQuestion("变量命名规范说法正确的是", map03, "B"))
.append(new ChoiceQuestion("以下()不是合法的标识符",map04, "C"))
.append(new ChoiceQuestion("表达式(11+3*8)/4%3的值是", map05, "D"))
.append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿"))
.append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼"))
.append(new AnswerQuestion("什么床不能睡觉", "牙床"))
.append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));
}
public String createPaper(String candidate, String number) throws CloneNotSupportedException {
QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
questionBankClone.setCandidate(candidate);
questionBankClone.setNumber(number);
return questionBankClone.toString();
}
}
```
## 测试验证
```java
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
QuestionBankController questionBankController = new QuestionBankController();
System.out.println(questionBankController.createPaper("花花", "1000001921032"));
System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));
System.out.println(questionBankController.createPaper("大宝", "1000001921987"));
}
}
```
```
考生:花花
考号:1000001921032
--------------------------------------------
一、选择题
第1题:以下()不是合法的标识符
A:STRING
B:de$f
C:void
D:x3x;
答案:C
第2题:表达式(11+3*8)/4%3的值是
A:31
B:0
C:2
D:1
答案:C
第3题:JAVA所定义的版本中不包括
A:JAVA2 EE
B:JAVA2 HE
C:JAVA2 SE
D:JAVA2 ME
E:JAVA2 Card
答案:B
第4题:变量命名规范说法正确的是
A:变量不能以数字作为开头;
B:变量由字母、下划线、数字、$符号随意组成;
C:A和a在java中是同一个变量;
D:不同类型的变量,可以起相同的名字;
答案:A
第5题:下列说法正确的是
A:JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来
B:JAVA程序中类名必须与文件名一样
C:JAVA程序中可以有多个main方法
D:JAVA程序的main方法必须写在类里面
答案:D
二、问答题
第1题:什么床不能睡觉
答案:牙床
第2题:铁棒打头疼还是木棒打头疼
答案:头最疼
第3题:小红马和小黑马生的小马几条腿
答案:4条腿
第4题:为什么好马不吃回头草
答案:后面的草没了
```
## 总结
- 以上的实际场景模拟了原型模式在开发中重构的作用,但是原型模式的使用频率确实不是很高。如果有一些特殊场景需要使用到,也可以按照此设计模式进行优化。
- 另外原型设计模式的优点包括:便于通过克隆方式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有一些缺点如果对象中包括了循环引用的克隆,以及类中深度使用对象的克隆,都会使此模式变得异常麻烦。
- 终究设计模式是一整套的思想,在不同的场景合理的运用可以提升整体的架构的质量。永远不要想着去硬凑设计模式,否则将会引起过渡设计,以及在承接业务反复变化的需求时造成浪费的开发和维护成本。
- 初期是代码的优化,中期是设计模式的使用,后期是把控全局服务的搭建。不断的加强自己对全局能力的把控,也加深自己对细节的处理。可上可下才是一个程序员最佳处理方式,选取最合适的才是最好的选择。
## 定义
**适配器模式**:定义一个包装类,用于包装不兼容接口的对象
> 1. 包装类 = 适配器 Adapter;
> 2. 被包装对象 = 适配者 Adaptee = 被适配的类
![](img/01.png)
## 示例
- 背景:买了一个进口笔记本电脑
- 冲突:笔记本电脑需要的三项电源,和只提供的二项电源冲突
- 解决方案:设置一个适配器二项充电口转化为三项充电口
**Adaptee 原有的类 提供二项电**
```java
public class TwoPower {
public void powerByTwo() {
System.out.println("提供二项供电");
}
}
```
**Target 目标类 能输出三项供电**
```java
public interface ThreePower {
/**
* 三项供电
*/
void powerByThree();
}
```
**对象适配器,转换类 Adapter**
```java
public class TwoToThreeAdapter implements ThreePower{
/**
* 使用委托来完成适配
*/
private final TwoPower twoPower;
public TwoToThreeAdapter(TwoPower twoPower) {
this.twoPower = twoPower;
}
@Override
public void powerByThree() {
System.out.println("借助组合适配器转化二项电");
twoPower.powerByTwo();
}
}
```
**类适配器 转换类 Adapter**
```java
public class TwoToThreeAdapter2 extends TwoPower implements ThreePower{
@Override
public void powerByThree() {
System.out.println("借助继承适配器转化二项电");
this.powerByTwo();
}
}
```
**测试**
```java
/**
* 笔记本电脑 这是使用组合模式的-适配器模式
*/
public class NoteBook {
/**
* 期望的三项供电接口
*/
private final ThreePower threePower;
public NoteBook(ThreePower threePower) {
this.threePower = threePower;
}
public static void main(String[] args) {
System.out.println("=============== 继承方式的适配器使用 类适配器 ===============");
ThreePower threePower1 = new TwoToThreeAdapter2();
NoteBook noteBook1 = new NoteBook(threePower1);
noteBook1.recharge();
noteBook1.work();
System.out.println("=============== 组合方式的适配器使用 对象适配器 ===============");
// 现在只有二项供电
TwoPower twoPower = new TwoPower();
ThreePower threePower = new TwoToThreeAdapter(twoPower);
NoteBook noteBook = new NoteBook(threePower);
// 1. 充电
noteBook.recharge();
// 2. 工作
noteBook.work();
}
public void work() {
System.out.println("笔记本电脑开始工作!");
}
public void recharge() {
// 使用三项充电
threePower.powerByThree();
}
}
```
> 本部分内容参见[这里](https://baijiahao.baidu.com/s?id=1639383166201396225&wfr=spider&for=pc)
## 定义
桥接模式(Bridge Pattern),将抽象部分与它的实现部分分离,使它们都可以独立地变化。更容易理解的表述是:实现系统可从多种维度分类,桥接模式将各维度抽象出来,各维度独立变化,之后可通过聚合,将各维度组合起来,减少了各维度间的耦合。
## 示例
我们都去买过手机,手机按照品牌分可以分为华为、小米、OPPO、vivo 等品牌,如果这些手机按照内存分又可以分为 4 G、6 G、8 G等等。假如我们每一种手机都想要玩一下,至少需要 4×3 个。这对我们来说这些手机也太多了,竟然有 12 个,最主要的是手机品牌和内存是放在一起的。现在有这样一种机制,手机牌品商是一个公司,做手机内存的是一个公司,想要做什么手机我们只需要让其两者搭配起来即可。有点类似于全球贸易分工明确的思想,这就是桥接模式,把两个不同维度的东西桥接起来。
从上面的例子我们可以看到,我们的手机可以从两个维度进行变化,一个是品牌,一个是内存。此时我们就可以通过桥接模式将这两个维度分离开来,每一个维度都可以独立扩展。比如说手机品牌,可以又出现了苹果、三星、锤子等等。内存方面又可以生产 10 G、16 G 的了。从专业的角度来看可以这样定义桥接模式:
> 桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。
上面的例子我们画一张类图来表示一下:
![](img/01.jpeg)
基本上意思就是这,也就是我们买手机的时候有两个维度可供我们选择:一个是品牌一个是内存。
(1)Client:指的是我们买手机的人
(2)Abstraction(抽象类):指的是手机抽象类
(3)Refined Abstraction(具体类):指的是具体手机品牌
(4)Implementor:在这里相当于手机的其他组件,内存
(5)Concrete Implementor:具体的内存型号
## 实现
第一步:定义 Implementor,这里定义手机内存接口
```java
public interface Memory {
void addMemory();
}
```
第二步:定义 Concrete Implementor,这里指具体的内存
内存这里定义了两种一种是 6 G,一种是 8 G
```java
public class Memory6G implements Memory{
@Override
public void addMemory() {
System.out.println("手机安装了6G内存");
}
}
// ---------------------------------------
public class Memory8G implements Memory{
@Override
public void addMemory() {
System.out.println("手机安装了8G内存");
}
}
```
第三步:定义 Abstraction 手机抽象类
```java
public abstract class Phone {
protected Memory phoneMemory;
public void setPhoneMemory(Memory phoneMemory) {
this.phoneMemory = phoneMemory;
}
public abstract void buyPhone();
}
```
第四步:定义Refined Abstraction(具体的手机品牌)
首先是华为
```java
public class HuaWei extends Phone{
@Override
public void buyPhone() {
phoneMemory.addMemory();
System.out.println("购买华为手机");
}
}
```
然后是小米
```java
public class Mi extends Phone{
@Override
public void buyPhone() {
phoneMemory.addMemory();
System.out.println("购买小米手机");
}
}
```
第五步:测试一下
```java
public class Client {
public static void main(String[] args) {
// 华为8G手机
System.out.println("====== 华为8G手机 ======");
HuaWei huaWei = new HuaWei();
huaWei.setPhoneMemory(new Memory8G());
huaWei.buyPhone();
// 小米6G手机
System.out.println("====== 小米6G手机 ======");
Mi mi = new Mi();
mi.setPhoneMemory(new Memory6G());
mi.buyPhone();
}
}
```
```
====== 华为8G手机 ======
手机安装了8G内存
购买华为手机
====== 小米6G手机 ======
手机安装了6G内存
购买小米手机
```
从代码就可以看出,购买手机的时候,品牌和内存两个维度是分开的。下面我们分析一下这个桥接模式
## 分析桥接模式
### 为什么使用桥接模式不使用继承呢?
继承是一种强耦合关系,子类与父类有非常紧密的依赖关系,父类的任何变化都会导致子类发生变化。因此才使用桥接模式,使用了桥接模式之后,我们的两个维度就像桥梁一样被链接了起来。体现了松耦合的特性。
### 桥接模式的优点
1. 分离抽象和实现部分:把手机、内存抽象出来。实现与之分离。
2. 松耦合:两个维度分开
3. 单一职责原则:每个维度各干各的活
关于桥接模式的使用场景我觉得你只需要知道他的思想,然后在遇到问题的时候能够想到这种模式即可。
在现实生活中,存在很多“部分-整体”的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣服与衣柜、以及厨房中的锅碗瓢盆等。在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。
## 定义
组合模式,将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
![](img/01.jpg)
组合模式中的角色:
- **Component 抽象组件**:为组合中所有对象提供一个接口,不管是叶子对象还是组合对象。
- **Composite 组合节点对象**:实现了 Component 的所有操作,并且持有子结点对象。
- **Leaf 叶结点对象**:叶结点对象没有任何子结点,实现了 Component 中的某些操作。
组合模式的主要优点有:
1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”
其主要缺点是:
1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系
2. 不容易限制容器中的构件
3. 不容易用继承的方法来增加构件的新功能
## 组合模式的结构与实现
组合模式分为透明式的组合模式和安全式的组合模式。
### 透明方式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 add()、remove() 及 getChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
![](img/02.gif)
### 安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
![](img/03.gif)
## 使用场景
1. 在需要表示一个对象整体与部分的层次结构的场合
2. 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合
## 示例——公司管理系统
![](img/04.png)
【公司类——抽象类或接口】
```java
public abstract class Company {
protected String name;
public Company(String name) {
this.name = name;
}
public abstract void add(Company c); // 增加
public abstract void remove(Company c); // 移除
public abstract void display(int depth); // 显示
public abstract void lineOfDuty(); // 履行职责
}
```
【具体公司类——实现接口、树枝节点】
```java
public class ConcreteCompany extends Company {
private final List<Company> children = new ArrayList<>();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company c) {
children.add(c);
}
@Override
public void remove(Company c) {
children.remove(c);
}
@Override
public void display(int depth) {
super.display(depth);
for (Company component : children) {
component.display(depth + 2);
}
}
@Override
public void lineOfDuty() {
for (Company component : children) {
component.lineOfDuty();
}
}
}
```
【人力资源部】
```java
public class HRDepartment extends Company{
public HRDepartment(String name) {
super(name);
}
@Override
public void add(Company c) {
}
@Override
public void remove(Company c) {
}
@Override
public void lineOfDuty() {
System.out.println(name + "员工招聘培训管理");
}
}
```
【财务部】
```java
public class FinanceDepartment extends Company{
public FinanceDepartment(String name) {
super(name);
}
@Override
public void add(Company c) {
}
@Override
public void remove(Company c) {
}
@Override
public void lineOfDuty() {
System.out.println(name + "公司财务收支管理");
}
}
```
【测试类】
```java
public class Test {
public static void main(String[] args) {
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.add(new HRDepartment("总公司人力资源部"));
root.add(new FinanceDepartment("总公司财务部"));
ConcreteCompany comp = new ConcreteCompany("上海华东分公司");
comp.add(new HRDepartment("上海华东分公司人力资源部"));
comp.add(new FinanceDepartment("上海华东分公司财务部"));
root.add(comp);
ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
comp1.add(new HRDepartment("南京办事处人力资源部"));
comp1.add(new FinanceDepartment("南京办事处财务部"));
comp.add(comp1);
ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
comp2.add(new HRDepartment("杭州办事处人力资源部"));
comp2.add(new FinanceDepartment("杭州办事处财务部"));
comp.add(comp2);
System.out.println("\n结构图:");
root.display(1);
System.out.println("\n职责:");
root.lineOfDuty();
}
}
```
输出结果:
```
结构图:
-北京总公司
---总公司人力资源部
---总公司财务部
---上海华东分公司
-----上海华东分公司人力资源部
-----上海华东分公司财务部
-----南京办事处
-------南京办事处人力资源部
-------南京办事处财务部
-----杭州办事处
-------杭州办事处人力资源部
-------杭州办事处财务部
职责:
总公司人力资源部员工招聘培训管理
总公司财务部公司财务收支管理
上海华东分公司人力资源部员工招聘培训管理
上海华东分公司财务部公司财务收支管理
南京办事处人力资源部员工招聘培训管理
南京办事处财务部公司财务收支管理
杭州办事处人力资源部员工招聘培训管理
杭州办事处财务部公司财务收支管理
```
> 本文参考[设计模式之装饰器模式](https://www.cnblogs.com/yssjun/p/11110013.html)
装饰器模式主要对现有的类对象进行包裹和封装,以期望在不改变类对象及其类定义的情况下,为对象添加额外功能。是一种对象结构型模式。需要注意的是,该过程是通过调用被包裹之后的对象完成功能添加的,而不是直接修改现有对象的行为,相当于增加了中间层。类似于python中的@装饰器。
装饰器模式可以动态的为同一类的不同对象加以修饰以添加新的功能,灵活的对类对象功能进行扩展。
## 优缺点
**优点:**
1. 相比较于类的继承来扩展功能,对对象进行包裹更加的灵活;
2. 装饰类和被装饰类相互独立,耦合度较低;
**缺点:**
1. 没有继承结构清晰
2. 包裹层数较多时,难以理解和管理
## 应用场景
- 动态的增加对象的功能
- 不能以派生子类的方式来扩展功能
- 限制对象的执行条件
- 参数控制和检查等
## 原理
![](img/01.png)
**Component:**
 对象的接口类,定义装饰对象和被装饰对象的共同接口
**ConcreteComponent:**
 被装饰对象的类定义
**Decorator:**
 装饰对象的抽象类,持有一个具体的被修饰对象,并实现接口类继承的公共接口
**ConcreteDecorator:**
 具体的装饰器,负责往被装饰对象添加额外的功能
## 示例
### 画图
系统中存在一个画圆的类,该类只是用来画圆,以及其他一些大小和位置等参数的控制。
**新加需求:**
- 可以对圆的边进行着色
- 可以对圆填充颜色
- 可以同时对边和内部着色
这个需求的常规方法实现可能如下:
1. 对画圆类进行迭代,以支持边和内部颜色填充
2. 画圆类作为父类,分别定义三个子类,继承父类的画圆方法,子类分别实现对应的作色需求
上面的两个方法都是可行的,也是比较直观的,这里我们尝试使用装饰器模式来实现,作为以上两种方法的对比。
![](img/02.png)
【接口类:shape】
```java
public interface Shape {
void draw();
}
```
【 画圆类:Circle】
```java
public class Circle implements Shape {
@Override
public void draw() {
System.out.print("a circle!");
}
}
```
【抽象装饰器类:Decorator】
```java
public abstract class Decorator implements Shape {
protected Shape circle;
public Decorator(Shape shape) {
circle = shape;
}
public void draw() {
circle.draw();
}
}
```
【为圆边着色装饰器类:CircleEdge】
```java
public class CircleEdge extends Decorator {
public CircleEdge(Shape circle) {
super(circle);
}
private void setEdgeColor() {
System.out.print(", edge with color");
}
public void draw() {
circle.draw();
setEdgeColor();
}
}
```
【为圆填充颜色装饰器类:CircleEdge】
```java
public class CircleFill extends Decorator {
public CircleFill(Shape circle) {
super(circle);
}
private void setEdgeFill() {
System.out.print(", content with color");
}
public void draw() {
circle.draw();
setEdgeFill();
}
}
```
【测试类】
```java
public class Test {
public static void main(String[] args) {
Shape circle = new Circle();
circle.draw();
System.out.println("");
Decorator circleEdge = new CircleEdge(circle);
circleEdge.draw();
System.out.println("");
Decorator circleFill = new CircleFill(circle);
circleFill.draw();
System.out.println("");
Decorator circleEdgeFill = new CircleFill(circleEdge);
circleEdgeFill.draw();
}
}
```
输出结果:
```
a circle!
a circle!, edge with color
a circle!, content with color
a circle!, edge with color, content with color
```
### 网络数据报封装
接下来我们在使用网络数据传输的例子来体会一下装饰器模式,下图表示的是应用层的文件传输协议FTP通过TCP来传输数据:
![](img/03.png)
虽然应用层可以越过传输层直接使用网络层进行数据发送(如,ICMP),但多数都会使用传输层的TCP或者UDP进行数据传输的。
下面我们用装饰器模式来表示一下应用层数据通过传输层来发送数据,UML 类图如下:
![](img/04.png)
上述图中表示了,应用层的数据通过添加 TCP 头或者 UDP 头,然后通过下面的网络层 send 数据。
【 数据报接口类:Datagram】
```java
public interface Datagram {
void send(); // 通过网络层发送IP数据报
}
```
【应用层数据类:AppDatagram】
```java
public class AppDatagram implements Datagram {
@Override
public void send() {
System.out.print("send IP datagram!");
}
}
```
【传输层类(抽象装饰器):TransportLayer】
```java
public abstract class TransportLayer implements Datagram {
protected Datagram appData;
public TransportLayer(Datagram appData) {
this.appData = appData;
}
public void send() {
appData.send();
}
}
```
【添加TCP头部类:UseTCP】
```java
public class UseTCP extends TransportLayer{
public UseTCP(Datagram appData) {
super(appData);
}
private void addHeader() {
System.out.print("Appdata add TCP header, ");
}
public void send() {
addHeader();
appData.send();
}
}
```
【添加TCP头部类:UseUDP】
```java
public class UseUDP extends TransportLayer{
public UseUDP(Datagram appData) {
super(appData);
}
private void addHeader() {
System.out.print("Appdata add UDP header, ");
}
public void send() {
addHeader();
appData.send();
}
}
```
【测试类】
```java
public class Test {
public static void main(String[] args) {
Datagram appData = new AppDatagram();
appData.send();
System.out.println();
TransportLayer tcpData = new UseTCP(appData);
tcpData.send();
System.out.println();
TransportLayer udpData = new UseUDP(appData);
udpData.send();
}
}
```
输出结果:
```
send IP datagram!
Appdata add TCP header, send IP datagram!
Appdata add UDP header, send IP datagram!
```
## 总结
其实所谓装饰器,本质上是对现有类对象的包裹,得到一个加强版的对象。
和 Python 中 @ 装饰器不同的是:
1. python中的装饰器是作用于函数或者类定义的,并直接覆盖掉了原来函数或者类的定义
2. 装饰器模式仅仅是修改了了已经产生的对象的行为,和类定义没有半点关系
通过上面的两个例子,应该对装饰器模式有了一个简单的认识。
另外,要体会到什么时候用继承什么时候用装饰器。
外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。
外观模式又称为门面模式,它是一种对象结构型模式。外观模式是[迪米特法则](https://so.csdn.net/so/search?from=pc_blog_highlight&q=迪米特法则)的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。
外观模式包含如下两个角色:
- **Facade(外观角色)**:在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
- **SubSystem(子系统角色)**:在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。
外观模式的目的不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
外观模式的本质是:**封装交互,简化调用**
---
`SLF4J` 是简单的日志外观模式框架,抽象了各种日志框架例如 `Logback``Log4j``Commons-logging``JDK` 自带的 `logging` 实现接口。它使得用户可以在部署时使用自己想要的日志框架。
`SLF4J` **没有替代任何日志框架,它仅仅是标准日志框架的外观模式**。如果在类路径下除了 `SLF4J` 再没有任何日志框架,那么默认状态是在控制台输出日志。
> 日志处理框架 Logback 是 Log4j 的改进版本,原生支持SLF4J(因为是同一作者开发的),因此 Logback+SLF4J 的组合是日志框架的最佳选择,比 SLF4J+其它日志框架 的组合要快一些。而且Logback的配置可以是XML或Groovy代码。
SLF4J 的 helloworld 如下:
```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.error("Hello World");
}
}
```
如果添加的是 Logback 实现:
```xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
```
输出为:
```
14:09:45.561 [main] ERROR com.ice.structure.Facade.HelloWorld - Hello World
```
如果是 Log4j:
```xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
```
需要有配置文件:
```properties
#日志级别大小: DEBUG < INFO < WARN < ERROR < FATAL
#log4j.rootLogger 配置的是大于等于当前级别的日志信息的输出
#log4j.rootLogger 用法:(注意appenderName可以是一个或多个)
#log4j.rootLogger = 日志级别,appenderName1,appenderName2,....
#log4j.appender.appenderName1定义的是日志的输出方式,有两种:一种是命令行输出或者叫控制台输出,另一种是文件方式保存
# 1)控制台输出则应该配置为org.apache.log4j.PatternLayout
# 2)文本方式保存应该配置为org.apache.log4j.DailyRollingFileAppender
# 3)也可以自定义 Appender类
#log4j.appender.appenderName1.layout.ConversionPattern 定义的是日志内容格式
#log4j.appender.appenderName1.file 定义了该日志文件的文件名称
#log4j.appender.appenderName1.DatePattern 定义了日志文件重新生成的时间间隔,如果设置到天,则每天重新生成一个新的日志文件。
# 旧的日志文件则以新的文件名保存,文件名称 = log4j.appender.appenderName1.file + log4j.appender.appenderName1.DatePattern
#log4j.rootLogger = info,stdout,file
log4j.rootLogger = info,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%C{1}:%L] - %m%n
#log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.file.file=d\:\\log\\info(+).log
#log4j.appender.file.DatePattern= '.'yyyy-MM-dd
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%C{1}:%L] - %m%n
# log4j.logger 用法如下
# 1)log4j.logger.包名 = 日志级别 , appenderName1,appenderName2,....
# 定义该包名下的所有类的日志输出
# 2)log4j.logger.类全名含包名 = 日志级别 , appenderName1,appenderName2,....
# 定义指定类的日志输出
# 3) log4j.logger.日志对象Logger命名名称 = 日志级别 , appenderName1,appenderName2,....
# 定义了某命名名称的日志的 输出,如:
# log4j.logger.Log1 就是指定义通过 Logger.getLogger("Log1") 获取的日志对象的日志输出
log4j.logger.edu.mvcdemo.controller = debug,controller_logfile
log4j.appender.controller_logfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.controller_logfile.file=d\:\\log\\controller_logfile.log
log4j.appender.controller_logfile.DatePattern= '.'yyyy-MM-dd
log4j.appender.controller_logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.controller_logfile.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}][%C{1}:%L] - %m%n
```
输出结果为:
```
[INFO][2021-10-30 14:22:34][HelloWorld:10] - Hello World
```
首先常规操作,我们先看一下什么是享元模式,维基百科解释:享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
享元模式的使用场景还是比较多的,最常见的一个场景就是 Java JDK 里面的 String 字符串类,因为 JVM 中有常量池,常量池的实现就是一种享元模式,避免多个相同对象的存在。另外线程池以及很多用到缓冲池的地方都采用了享元模式,比如 Integer 类中默认缓存了-128 -127 之间的整数,等等场景。
**代理模式**是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
## 静态代理
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理着你是角色,代理真实角色后,我们一般会做一些复数操作
- 客户:访问代理对象的人
以租房举例:
![](img/静态代理.png)
【抽象角色】
```java
public interface Rent {
public void rent();
}
```
【真实角色】
```
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要租房子了");
}
}
```
【代理角色】
```java
public class Agent implements Rent{
private Host host;
public Agent(Host host) {
this.host = host;
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void fare(){
System.out.println("收中介费");
}
@Override
public void rent() {
host.rent();
}
}
```
【客户】
```java
public class Client {
public static void main(String[] args) {
Host host = new Host();
Agent proxy = new Agent(host);
proxy.seeHouse();
proxy.rent();
proxy.fare();
}
}
```
输出结果:
```
中介带你看房
房东要租房子了
收中介费
```
**代理模式的优点:**
- 可以使真实角色的操作哦更加纯粹,不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工
- 公共业务扩展的时候,方便集中管理
**缺点**
- 一个真实角色就会产生一个代理角色,代码量会翻倍
## 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分两大类:**基于接口的动态代理****基于类的动态代理**
- 基于接口:JDK 动态代理
- 基于类:cglib
- java 字节码实现:javasist
还是以租房为例:
### JDK 动态代理
需要了解两个类:`Proxy``InvocationHandler`
【代理角色】
```java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
// 动态生成代理对象
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
@Override
// 处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理中介带你看房");
Object result = method.invoke(rent, args);
System.out.println("收中介费");
return result;
}
}
```
【客户】
```java
public class Client2 {
public static void main(String[] args) {
// 真实角色
Host host = new Host();
// 代理角色:现在还没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
// 生成代理类实例,即代理对象
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
```
**通用动态代理处理类**
```java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandler implements InvocationHandler {
// 被动态代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 动态生成代理对象
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
// 处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// before call method do something
Object result = method.invoke(target, args);
// after call method do something
return result;
}
}
```
### CGLib 动态代理
CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。
【jar包】
```xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
```
【代理类】
```java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLibDynamicProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("rent")) {
System.out.println("代理中介带你看房");
methodProxy.invokeSuper(o, args);
System.out.println("收中介费");
}
return null;
}
public Object getInstance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Host.class); // 这里不需要搞个接口了,cglib支持基于类的动态代理
enhancer.setCallback(this);
return enhancer.create();
}
}
```
【客户】
```java
public class Client3 {
public static void main(String[] args) {
CGLibDynamicProxy proxy = new CGLibDynamicProxy();
Rent rent = (Rent) proxy.getInstance();
rent.rent();
}
}
```
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
design-pattern
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="design-pattern" />
<module name="design pattern" />
</profile>
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AliAccessStaticViaInstance" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AliArrayNamingShouldHaveBracket" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AliControlFlowStatementWithoutBraces" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AliDeprecation" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AliEqualsAvoidNull" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AliLongLiteralsEndingWithLowercaseL" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AliMissingOverrideAnnotation" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AliWrapperTypeEquality" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaAbstractClassShouldStartWithAbstractNaming" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAbstractMethodOrInterfaceMethodMustUseJavadoc" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidApacheBeanUtilsCopy" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidCallStaticSimpleDateFormat" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidCommentBehindStatement" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidComplexCondition" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidConcurrentCompetitionRandom" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidDoubleOrFloatEqualCompare" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidManuallyCreateThread" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidMissUseOfMathRandom" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidNegationOperator" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidNewDateGetTime" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidPatternCompileInMethod" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidReturnInFinally" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidStartWithDollarAndUnderLineNaming" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaAvoidUseTimer" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaBigDecimalAvoidDoubleConstructor" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaBooleanPropertyShouldNotStartWithIs" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaClassCastExceptionWithSubListToArrayList" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaClassCastExceptionWithToArray" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaClassMustHaveAuthor" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaClassNamingShouldBeCamel" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaCollectionInitShouldAssignCapacity" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaCommentsMustBeJavadocFormat" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaConcurrentExceptionWithModifyOriginSubList" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaConstantFieldShouldBeUpperCase" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaCountDownShouldInFinally" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaDontModifyInForeachCircle" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaEnumConstantsMustHaveComment" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaExceptionClassShouldEndWithException" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaIbatisMethodQueryForList" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaLockShouldWithTryFinally" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaLowerCamelCaseVariableNaming" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaMethodReturnWrapperType" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaMethodTooLong" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaPackageNaming" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaPojoMustOverrideToString" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaPojoMustUsePrimitiveField" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaPojoNoDefaultValue" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaRemoveCommentedCode" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaServiceOrDaoClassShouldEndWithImpl" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaStringConcat" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaSwitchStatement" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaTestClassShouldEndWithTestNaming" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaThreadLocalShouldRemove" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaThreadPoolCreation" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaThreadShouldSetName" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaTransactionMustHaveRollback" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaUndefineMagicConstant" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaUnsupportedExceptionWithModifyAsList" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaUseQuietReferenceNotation" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaUseRightCaseForDateFormat" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="TOP_LEVEL_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="INNER_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="METHOD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
</value>
</option>
<option name="FIELD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="IGNORE_DEPRECATED" value="false" />
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="date,description,blog,create" />
</inspection_tool>
<inspection_tool class="MapOrSetKeyShouldOverrideHashCodeEquals" enabled="false" level="CRITICAL" enabled_by_default="false" />
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public" />
</remote-repository>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SmartFoxProjectConfig">
<option name="projectInspectionClosed" value="true" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ice</groupId>
<artifactId>design-pattern</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- sl4j日志包 -->
<!-- slf4j-log4j12-版本号.jar 依赖以下两个包,也会自动添加进来
slf4j-api-版本号.jar
log4j-版本号.jar
例如:slf4j-log4j12-1.7.2.jar,就依赖slf4j-api-1.7.2.jar和log4j-1.2.17.jar这两个包
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
<!-- end -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.ice.creation.AbstractFactory;
import com.ice.creation.AbstractFactory.factory.*;
import com.ice.creation.AbstractFactory.product.Chair;
import com.ice.creation.AbstractFactory.product.CoffeeTable;
import com.ice.creation.AbstractFactory.product.Sofa;
import java.util.NoSuchElementException;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:51:49
*/
public class Client {
private FurnitureFactory factory;
public Client(FurnitureStyle style) {
if (style == FurnitureStyle.Modern) {
this.factory = new ModernLine();
} else if (style == FurnitureStyle.Victorian) {
this.factory = new VictorianLine();
} else if (style == FurnitureStyle.ArtDeco) {
this.factory = new ArtDecoLine();
} else {
throw new NoSuchElementException("没有此种风格生产线");
}
}
public void purchaseFurniture() {
Chair chair = factory.createChair();
Sofa sofa = factory.createSofa();
CoffeeTable coffeeTable = factory.createCoffeeTable();
chair.info();
sofa.info();
coffeeTable.info();
}
}
package com.ice.creation.AbstractFactory;
import com.ice.creation.AbstractFactory.factory.FurnitureStyle;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:58:25
*/
public class Test {
public static void main(String[] args) {
Client client = new Client(FurnitureStyle.Victorian);
client.purchaseFurniture();
}
}
package com.ice.creation.AbstractFactory.factory;
import com.ice.creation.AbstractFactory.product.*;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:40:41
*/
public class ArtDecoLine implements FurnitureFactory {
@Override
public Chair createChair() {
return new ArtDecoChair();
}
@Override
public Sofa createSofa() {
return new ArtDecoSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new ArtDecoCoffeeTable();
}
}
package com.ice.creation.AbstractFactory.factory;
import com.ice.creation.AbstractFactory.product.Chair;
import com.ice.creation.AbstractFactory.product.CoffeeTable;
import com.ice.creation.AbstractFactory.product.Sofa;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:21:52
*/
public interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
CoffeeTable createCoffeeTable();
}
package com.ice.creation.AbstractFactory.factory;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:26:11
*/
public enum FurnitureStyle {
Modern,
Victorian,
ArtDeco
}
package com.ice.creation.AbstractFactory.factory;
import com.ice.creation.AbstractFactory.product.*;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:40:41
*/
public class ModernLine implements FurnitureFactory{
@Override
public Chair createChair() {
return new ModernChair();
}
@Override
public Sofa createSofa() {
return new ModernSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new ModernCoffeeTable();
}
}
package com.ice.creation.AbstractFactory.factory;
import com.ice.creation.AbstractFactory.product.*;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:40:41
*/
public class VictorianLine implements FurnitureFactory {
@Override
public Chair createChair() {
return new VictorianChair();
}
@Override
public Sofa createSofa() {
return new VictorianSofa();
}
@Override
public CoffeeTable createCoffeeTable() {
return new VictorianCoffeeTable();
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:15:36
*/
public class ArtDecoChair extends Chair{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的椅子");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:22:13
*/
public class ArtDecoCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的咖啡桌");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:20:18
*/
public class ArtDecoSofa extends Sofa{
@Override
public void info() {
System.out.println("这是装饰风艺术风格的沙发");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:13:10
*/
public abstract class Chair{
public abstract void info();
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:14:15
*/
public abstract class CoffeeTable{
public abstract void info();
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:09:58
*/
public class ModernChair extends Chair{
@Override
public void info() {
System.out.println("这是现代风格的椅子");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:21:01
*/
public class ModernCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是现代风格的咖啡桌");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:17:55
*/
public class ModernSofa extends Sofa{
@Override
public void info() {
System.out.println("这是现代风格的沙发");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 09:13:53
*/
public abstract class Sofa {
public abstract void info();
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:15:09
*/
public class VictorianChair extends Chair{
@Override
public void info() {
System.out.println("这是维多利亚风格的椅子");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:21:36
*/
public class VictorianCoffeeTable extends CoffeeTable{
@Override
public void info() {
System.out.println("这是维多利亚风格的咖啡桌");
}
}
package com.ice.creation.AbstractFactory.product;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-13 10:19:51
*/
public class VictorianSofa extends Sofa{
@Override
public void info() {
System.out.println("这是维多利亚风格的沙发");
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:19:14
*/
public class Bottle implements Packable {
@Override
public String getPack() {
return "纸杯";
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:19:27
*/
public abstract class Burger implements Item {
@Override
public Packable packing() {
return new Wrapper();
}
@Override
public abstract float getPrice();
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:23:20
*/
public class Burger1 extends Burger {
@Override
public String getName() {
return "汉堡1";
}
@Override
public float getPrice() {
return 25.0f;
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:23:20
*/
public class Burger2 extends Burger {
@Override
public String getName() {
return "汉堡2";
}
@Override
public float getPrice() {
return 35.0f;
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:29:45
*/
public class Consumer {
public static void main(String[] args) {
Package set = new Package();
// 购买第一个套餐
Meal order1 = set.package1();
System.out.println("------order1------");
order1.showItem();
System.out.println("总额:¥" + order1.getCost());
// 购买第二个套餐
Meal order2 = set.package2();
System.out.println("------order2------");
order2.showItem();
System.out.println("总额:¥" + order2.getCost());
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:19:38
*/
public abstract class Drink implements Item {
@Override
public Packable packing() {
return new Bottle();
}
@Override
public abstract float getPrice();
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:23:45
*/
public class Drink1 extends Drink {
@Override
public String getName() {
return "饮品1";
}
@Override
public float getPrice() {
return 15.0f;
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:23:45
*/
public class Drink2 extends Drink {
@Override
public String getName() {
return "饮品2";
}
@Override
public float getPrice() {
return 18.0f;
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:18:24
*/
public interface Item {
//获取食物名称
public String getName();
//获取包装
public Packable packing();
//获取价格
public float getPrice();
}
\ No newline at end of file
package com.ice.creation.Builder;
import java.util.ArrayList;
import java.util.List;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:24:09
*/
public class Meal {
private final List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
//获取总消费
public float getCost() {
float cost = 0.0f;
for (Item item : items) {
cost += item.getPrice();
}
return cost;
}
public void showItem() {
for (Item item : items) {
System.out.print("餐品:" + item.getName());
System.out.print(",包装:" + item.packing().getPack());
System.out.println(",价格:¥" + item.getPrice());
}
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:18:48
*/
public interface Packable {
//获取包装类型
public String getPack();
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:24:31
*/
public class Package {
//订单1
public Meal package1() {
Meal meal = new Meal();
meal.addItem(new Burger1());
meal.addItem(new Drink1());
return meal;
}
//订单2
public Meal package2() {
Meal meal = new Meal();
meal.addItem(new Burger2());
meal.addItem(new Drink2());
return meal;
}
}
package com.ice.creation.Builder;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-15 21:19:04
*/
public class Wrapper implements Packable {
@Override
public String getPack() {
return "纸盒";
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 11:06:12
*/
public abstract class Logistics {
protected abstract Transport createTransport();
public void playDelivery(){
Transport transport = createTransport();
System.out.println("物流开始....");
transport.deliver();
System.out.println("物流结束....");
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 12:29:58
*/
public class RoadLogistics extends Logistics{
@Override
protected Transport createTransport() {
return new Truck();
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 12:29:58
*/
public class SeaLogistics extends Logistics{
@Override
protected Transport createTransport() {
return new Ship();
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 11:04:54
*/
public class Ship implements Transport {
@Override
public void deliver() {
System.out.println("轮船运输中...");
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 12:31:23
*/
public class Test {
public static void main(String[] args) {
System.out.println("======第一次物流======");
Logistics seaLogistics = new SeaLogistics();
seaLogistics.playDelivery();
System.out.println("======第二次物流======");
Logistics roadLogistics = new RoadLogistics();
roadLogistics.playDelivery();
}
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 10:58:16
*/
public interface Transport {
void deliver();
}
package com.ice.creation.FactoryPattern;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-08 11:01:12
*/
public class Truck implements Transport {
@Override
public void deliver() {
System.out.println("货车运输中...");
}
}
package com.ice.creation.Prototype;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 19:51:52
*/
public class AnswerQuestion {
private String name; // 问题
private String key; // 答案
public AnswerQuestion(String name, String key) {
this.name = name;
this.key = key;
}
public AnswerQuestion() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
package com.ice.creation.Prototype;
import java.util.Map;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 19:50:07
*/
public class ChoiceQuestion {
private String name;
private Map<String, String> option;
private String key;
public ChoiceQuestion(String name, Map<String, String> option, String key) {
this.name = name;
this.option = option;
this.key = key;
}
public ChoiceQuestion() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getOption() {
return option;
}
public void setOption(Map<String, String> option) {
this.option = option;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
package com.ice.creation.Prototype;
import com.ice.creation.Prototype.util.Topic;
import com.ice.creation.Prototype.util.TopicRandomUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 20:04:33
*/
public class QuestionBank implements Cloneable{
private String candidate; // 考生
private String number; // 考号
private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>();
private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<>();
public QuestionBank append(ChoiceQuestion choiceQuestion) {
choiceQuestionList.add(choiceQuestion);
return this;
}
public QuestionBank append(AnswerQuestion answerQuestion) {
answerQuestionList.add(answerQuestion);
return this;
}
@Override
public Object clone() throws CloneNotSupportedException {
QuestionBank questionBank = (QuestionBank) super.clone();
questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();
// 题目乱序
Collections.shuffle(questionBank.choiceQuestionList);
Collections.shuffle(questionBank.answerQuestionList);
// 答案乱序
ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
for (ChoiceQuestion question : choiceQuestionList) {
Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());
question.setOption(random.getOption());
question.setKey(random.getKey());
}
return questionBank;
}
public void setCandidate(String candidate) {
this.candidate = candidate;
}
public void setNumber(String number) {
this.number = number;
}
@Override
public String toString() {
StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
"考号:" + number + "\r\n" +
"--------------------------------------------\r\n" +
"一、选择题" + "\r\n\n");
for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
Map<String, String> option = choiceQuestionList.get(idx).getOption();
for (String key : option.keySet()) {
detail.append(key).append(":").append(option.get(key)).append("\r\n");;
}
detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
}
detail.append("二、问答题" + "\r\n\n");
for (int idx = 0; idx < answerQuestionList.size(); idx++) {
detail.append("第").append(idx + 1).append("题:").append(answerQuestionList.get(idx).getName()).append("\r\n");
detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
}
return detail.toString();
}
}
package com.ice.creation.Prototype;
import java.util.HashMap;
import java.util.Map;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 20:13:08
*/
public class QuestionBankController {
private QuestionBank questionBank = new QuestionBank();
public QuestionBankController() {
Map<String, String> map01 = new HashMap<>();
map01.put("A", "JAVA2 EE");
map01.put("B", "JAVA2 Card");
map01.put("C", "JAVA2 ME");
map01.put("D", "JAVA2 HE");
map01.put("E", "JAVA2 SE");
Map<String, String> map02 = new HashMap<>();
map02.put("A", "JAVA程序的main方法必须写在类里面");
map02.put("B", "JAVA程序中可以有多个main方法");
map02.put("C", "JAVA程序中类名必须与文件名一样");
map02.put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");
Map<String, String> map03 = new HashMap<>();
map03.put("A", "变量由字母、下划线、数字、$符号随意组成;");
map03.put("B", "变量不能以数字作为开头;");
map03.put("C", "A和a在java中是同一个变量;");
map03.put("D", "不同类型的变量,可以起相同的名字;");
Map<String, String> map04 = new HashMap<>();
map04.put("A", "STRING");
map04.put("B", "x3x;");
map04.put("C", "void");
map04.put("D", "de$f");
Map<String, String> map05 = new HashMap<>();
map05.put("A", "31");
map05.put("B", "0");
map05.put("C", "1");
map05.put("D", "2");
questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", map01, "D"))
.append(new ChoiceQuestion("下列说法正确的是", map02, "A"))
.append(new ChoiceQuestion("变量命名规范说法正确的是", map03, "B"))
.append(new ChoiceQuestion("以下()不是合法的标识符",map04, "C"))
.append(new ChoiceQuestion("表达式(11+3*8)/4%3的值是", map05, "D"))
.append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿"))
.append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼"))
.append(new AnswerQuestion("什么床不能睡觉", "牙床"))
.append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));
}
public String createPaper(String candidate, String number) throws CloneNotSupportedException {
QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
questionBankClone.setCandidate(candidate);
questionBankClone.setNumber(number);
return questionBankClone.toString();
}
}
package com.ice.creation.Prototype;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 20:14:53
*/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
QuestionBankController questionBankController = new QuestionBankController();
System.out.println(questionBankController.createPaper("花花", "1000001921032"));
// System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));
// System.out.println(questionBankController.createPaper("大宝", "1000001921987"));
}
}
package com.ice.creation.Prototype.util;
import java.util.Map;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 19:59:44
*/
public class Topic {
private Map<String, String> option; // 选项;A、B、C、D
private String key; // 答案;B
public Topic() {
}
public Topic(Map<String, String> option, String key) {
this.option = option;
this.key = key;
}
public Map<String, String> getOption() {
return option;
}
public void setOption(Map<String, String> option) {
this.option = option;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
package com.ice.creation.Prototype.util;
import java.util.*;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-25 19:59:01
*/
public class TopicRandomUtil {
/**
* 乱序Map元素,记录对应答案key
*
* @param option 题目
* @param key 答案
* @return Topic 乱序后 {A=c., B=d., C=a., D=b.}
*/
public static Topic random(Map<String, String> option, String key) {
Set<String> keySet = option.keySet();
List<String> keyList = new ArrayList<>(keySet);
Collections.shuffle(keyList);
Map<String, String> optionNew = new HashMap<>();
int idx = 0;
String keyNew = "";
for (String next : keySet) {
String randomKey = keyList.get(idx++);
if (key.equals(next)) {
keyNew = randomKey;
}
optionNew.put(randomKey, option.get(next));
}
return new Topic(optionNew, keyNew);
}
}
package com.ice.structure.Adapter;
/**
* 笔记本电脑 这是使用组合模式的-适配器模式
*/
public class NoteBook {
/**
* 期望的三项供电接口
*/
private final ThreePower threePower;
public NoteBook(ThreePower threePower) {
this.threePower = threePower;
}
public static void main(String[] args) {
System.out.println("=============== 继承方式的适配器使用 类适配器 ===============");
ThreePower threePower1 = new TwoToThreeAdapter2();
NoteBook noteBook1 = new NoteBook(threePower1);
noteBook1.recharge();
noteBook1.work();
System.out.println("=============== 组合方式的适配器使用 对象适配器 ===============");
// 现在只有二项供电
TwoPower twoPower = new TwoPower();
ThreePower threePower = new TwoToThreeAdapter(twoPower);
NoteBook noteBook = new NoteBook(threePower);
// 1. 充电
noteBook.recharge();
// 2. 工作
noteBook.work();
}
public void work() {
System.out.println("笔记本电脑开始工作!");
}
public void recharge() {
// 使用三项充电
threePower.powerByThree();
}
}
package com.ice.structure.Adapter;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-26 16:12:15
*/
public interface ThreePower {
/**
* 三项供电
*/
void powerByThree();
}
package com.ice.structure.Adapter;
/**
* @author ice
* @blog https://blog.csdn.net/dreaming_coder
* @description
* @create 2021-10-26 16:11:29
*/
public class TwoPower {
public void powerByTwo() {
System.out.println("提供二项供电");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册