设计模式
抽象工厂模式
抽象工厂模式是一种 创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类
- 图例:
-
详解:
-
抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的椅子都实现椅子接口; 所有**风格的咖啡桌都实现
咖啡桌
**接口, 以此类推。 -
接下来,我们需要声明抽象工厂所包含产品构造方法的接口,例如 $\color{Orange}{createChair创建椅子} $ ,$\color{Blue}{createSofa创建沙发} $ 和 $\color{RedOrange}{createCoffeeTable创建咖啡桌} $
这些方法必须返回抽象产品类型,即我们之前抽取的那些接口: 椅子,沙发和 咖啡桌等等。
- 对于那些产品变体情况下,我们都将基于
抽象工厂
接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如,$\color{Orange}{createChair创建椅子} $**ModernFurnitureFactory **只能创建 现代椅子ModernChair 、现代沙发ModernSofa、和现代咖啡桌ModernCoffeeTable对象
-
-
抽象工厂模式示意图
-
代码实现:
public interface Fruit {
public void get();
}
public abstract class Apple implements Fruit{
public abstract void get();
}
public class ChinaApple extends Apple {
@Override
public void get() {
System.out.println("中国的苹果...");
}
}
public interface FruitFactory {
//实例化苹果
public Fruit getApple();
//实例化香蕉
public Fruit getBanana();
}
public class ChinaFactory implements FruitFactory {
@Override
public Fruit getApple() {
return new ChinaApple();
}
@Override
public Fruit getBanana() {
return new ChinaBanana();
}
}
public class MainClass {
public static void main(String[] args){
//创建中国工厂
FruitFactory chinaFactory = new ChinaFactory();
//通过中国工厂生产中国苹果实例
Fruit apple = chinaFactory.getApple();
apple.get();
//通过中国工厂生产中国香蕉实例
Fruit banana = chinaFactory.getBanana();
banana.get();
//创建英国工厂
FruitFactory englandFactory = new EnglandFactory();
//通过英国工厂生产英国苹果实例
Fruit apple1 = englandFactory.getApple();
apple1.get();
//通过英国工厂生产英国香蕉实例
Fruit banana2 = englandFactory.getBanana();
banana2.get();
}
}
抽象工厂模式适合应用场景
如果代码需要与$\color{Blue}{多个不同系列} $ 的相关产品交互,但是由于无法提前获取相关信息,或者处于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用 抽象工厂
抽象工厂模式优缺点
优点:
- 你可以确保同一工厂生成的产品相互匹配。
- 你可以避免客户端和具体产品代码的耦合。
- 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
- 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。
缺点:
由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂
工厂方法模式
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
-
图列
-
问题:
假设你正在开发一款物流管理应用。 最初版本只能处理卡车运输, 因此大部分代码都在位于名为卡车的类中。
如果代码其余部分与现有类已经存在$\color{Mahogany}{耦合关系} $, 那么向程序中添加新类其实并没有那么容易。
- 解决方案:
工厂方法模式建议使用特殊的工厂方法代替对于对象构造函数的直接调用 (即使用 new
运算符)。不用担心, 对象仍将通过 new
运算符创建, 只是该运算符改在工厂方法中调用罢了。 工厂方法返回的对象通常被称作 “产品”。
-
详解:
卡 车 T r u c k \color{Mahogany}{卡车Truck} 卡车Truck和 轮 船 S h i p 类 \color{BrickRed}{轮船Ship类} 轮船Ship类 都必须实现 运 输 T r a n s p o r t 接 口 \color{OrangeRed}{运输Transport接口} 运输Transport接口,该接口声明了一个名为 d e l i v e r 交 付 的 方 法 \color{OrangeRed}{deliver交付的方法} deliver交付的方法。 每个类都将以不同的方式实现该方法: 卡车走陆路交付货物, 轮船走海路交付货物。 陆路运输RoadLogistics类中的工厂方法返回卡车对象,而海路运输SeaLogistics类则返回轮船对象。
-
结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T0rR3twf-1653183048680)(L:\Typora\typora-user-images\image-20220515201231402.png)]
-
代码实现:
package FactoryMethodPattern;
//雷锋 (1)抽象产品角色
public abstract class LeiFeng {
public abstract void Sweep();
public abstract void Wash();
public abstract void BuyRice();
}
package FactoryMethodPattern;
//社区志愿者
public class Volunteer extends LeiFeng {
@Override
public void Sweep() {
System.out.print("社区志愿者帮忙打扫 ");
}
@Override
public void Wash() {
System.out.print("社区志愿者帮忙洗衣服 ");
}
@Override
public void BuyRice() {
System.out.print("社区志愿者帮忙买米 ");
}
}
//大学生 (2)具体产品角色:
class Undergraduate extends LeiFeng {
@Override
public void Sweep() {
System.out.print("大学生帮忙打扫 ");
}
@Override
public void Wash() {
System.out.print("大学生帮忙洗衣服 ");
}
@Override
public void BuyRice() {
System.out.print("大学生帮忙买米 ");
}
}
package FactoryMethodPattern;
//雷锋工厂 (3)抽象工厂角色:
public interface IFactory {
LeiFeng CreateLeiFeng();
}
package FactoryMethodPattern;
//社区志愿者工厂 (4)具体工厂角色:
public class VolunteerFactory implements IFactory {
@Override
public LeiFeng CreateLeiFeng() {
return new Volunteer();
}
}
//学雷锋的大学生工厂
class UndergraduateFactory implements IFactory {
@Override
public LeiFeng CreateLeiFeng() {
return new Undergraduate();
}
}
package FactoryMethodPattern;
// 客户端测试代码
public class Test {
public static void main(String[] args) {
//要换成'社区志愿者',修改这里就可以
IFactory factory = new UndergraduateFactory();
LeiFeng student = factory.CreateLeiFeng();
student.Sweep();
student.Wash();
student.BuyRice();
System.out.println("\n --------------------------");
IFactory factory2 = new VolunteerFactory();
LeiFeng volunteer = factory2.CreateLeiFeng();
volunteer.Sweep();
volunteer.Wash();
volunteer.BuyRice();
}
}
工厂方法模式适合应用场景
当你在编写代码的过程中,如果 无 法 预 知 对 象 确 切 类 别 及 其 依 赖 关 系 \color{Red}{无法预知对象确切类别及其依赖关系} 无法预知对象确切类别及其依赖关系时,可使用工厂方法
例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。
工厂方法模式优缺点
优点:
- 你可以避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
- 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。
缺点:
应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。
生成器模式
生成器模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
- 图例:
-
详解:
-
该模式会将 对 象 构 造 过 程 划 分 为 一 组 \color{Red}{对象构造过程划分为一组} 对象构造过程划分为一组步骤, 比如 b u i l d W a l l s 创 建 墙 壁 \color{Mahogany}{buildWalls创建墙壁} buildWalls创建墙壁和 b u i l d D o o r 创 建 房 门 \color{Mahogany}{ buildDoor创建房门} buildDoor创建房门等。 每次创建对象时, 你都需要通过生成器对象执行一系列步骤。 重点在于你无需调用所有步骤, 而只需调用创建特定对象配置所需的那些步骤即可。
-
当你需要 创 建 不 同 形 式 的 产 品 \color{Red}{创建不同形式的产品} 创建不同形式的产品时, 其中的一些构造步骤可能需要不同的实现。 例如, 木屋的房门可能需要使用木头制造, 而城堡的房门则必须使用石头制造。
在这种情况下, 你可以 创 建 多 个 不 同 的 生 成 器 \color{Red}{创建多个不同的生成器} 创建多个不同的生成器, 用 不 同 方 式 实 现 一 组 相 同 的 创 建 步 骤 \color{YellowOrange}{用不同方式实现一组相同的创建步骤} 用不同方式实现一组相同的创建步骤。 然后你就可以在创建过程中使用这些生成器 (例如按顺序调用多个构造步骤) 来生成不同类型的对象。
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tlxQa51-1653183048681)(L:\Typora\builder-comic-1-zh.png)]
- 例如, 假设第一个建造者使用木头和玻璃制造房屋, 第二个建造者使用石头和钢铁, 而第三个建造者使用黄金和钻石。 在调用同一组步骤后, 第一个建造者会给你一栋普通房屋, 第二个会给你一座小城堡, 而第三个则会给你一座宫殿。 但是,只有在调用构造步骤的客户端代码可以通过通用接口与建造者进行交互时, 这样的调用才能返回需要的房屋。
- 你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为 单 独 的 主 管 类 \color{Mahogany}{单独的主管类} 单独的主管类。 主管类可定义创建步骤的执行顺序, 而生成器则提供这些步骤的实现。
- 生成器模式结构
-
代码实现
/** * Builder.java * 建造者 */ abstract class Builder { //地基 abstract void bulidA(); //钢筋工程 abstract void bulidB(); //铺电线 abstract void bulidC(); //粉刷 abstract void bulidD(); //完工-获取产品 abstract Product getProduct(); } /** * Product.java * 产品(房子) */ public class Product { private String buildA; private String buildB; private String buildC; private String buildD; public String getBuildA() { return buildA; } public void setBuildA(String buildA) { this.buildA = buildA; } public String getBuildB() { return buildB; } public void setBuildB(String buildB) { this.buildB = buildB; } public String getBuildC() { return buildC; } public void setBuildC(String buildC) { this.buildC = buildC; } public String getBuildD() { return buildD; } public void setBuildD(String buildD) { this.buildD = buildD; } @Override public String toString() { return buildA+"\n"+buildB+"\n"+buildC+"\n"+buildD+"\n"+"房子验收完成"; } } /** * ConcreteBuilder.java * 具体建造者(工人) */ public class ConcreteBuilder extends Builder{ private Product product; public ConcreteBuilder() { product = new Product(); } @Override void bulidA() { product.setBuildA("地基"); } @Override void bulidB() { product.setBuildB("钢筋工程"); } @Override void bulidC() { product.setBuildC("铺电线"); } @Override void bulidD() { product.setBuildD("粉刷"); } @Override Product getProduct() { return product; } } /** * Director.java * 指挥者 */ public class Director { //指挥工人按顺序造房 public Product create(Builder builder) { builder.bulidA(); builder.bulidB(); builder.bulidC(); builder.bulidD(); return builder.getProduct(); } } /** * Test.java * 测试类 */ public class Test { public static void main(String[] args) { Director director = new Director(); Product create = director.create(new ConcreteBuilder()); System.out.println(create.toString()); } }
生成器模式适合应用模式
- 使用生成器模式可避免**“重叠构造函数”**的出现
- 当你希望使用代码 创 建 不 同 形 式 的 产 品 \color{Peach}{创建不同形式的产品} 创建不同形式的产品(例如石头或木头房屋)时,可使用生成器模式
- 使用生成器 构 造 组 合 树 \color{Peach}{构造组合树} 构造组合树或其 他 复 杂 对 象 \color{Peach}{他复杂对象} 他复杂对象
生成器模式优缺点
优点:
- 你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
- 生成不同形式的产品时, 你可以复用相同的制造代码。
- 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。
缺点:
由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。
原型模式
原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。
- 图例:
-
详解:
如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于相同类的对象。 然后,你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。
不错! 但有个小问题。 并非所有对象都能通过这种方式进行复制, 因为有些对象 可 能 拥 有 私 有 成 员 变 量 \color{Red}{可能拥有私有成员变量} 可能拥有私有成员变量, 它们在对象本身以外是不可见的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMMZ0IEo-1653183048683)(L:\Typora\prototype-comic-1-zh.png)]
-
原型模式 将 克 隆 过 程 委 派 \color{Red}{将克隆过程委派} 将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个 通 用 接 口 \color{Melon}{通用接口} 通用接口,该接口让你能够克隆对象, 同时 又 无 需 将 代 码 和 对 象 所 属 类 耦 合 \color{Red}{又无需将代码和对象所属类耦合} 又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个 克隆方法。
所有的类对克隆方法的实现都非常相似。 该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。
-
其运作方式如下: 创 建 一 系 列 不 同 类 型 的 对 象 \color{Blue}{创建一系列不同类型的对象} 创建一系列不同类型的对象并不同的方式对其进行配置。 如果 所 需 对 象 与 预 先 配 置 的 对 象 \color{RubineRed}{所需对象与预先配置的对象} 所需对象与预先配置的对象相同, 那么你只需克隆原型即可, 无需新建一个对象。
-
原型模式结构
- 代码实现
/**
* 抽象原型角色
*/
public abstract class Prototype {
private String id;
public Prototype(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* 克隆自身的方法
* @return 一个从自身克隆出来的对象。
*/
public abstract Prototype clone();
}
/**
*具体原型角色
*/
public class ConcreteProtype1 extends Prototype {
public ConcreteProtype1(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcreteProtype1(this.getId());
return prototype;
}
}
public class ConcreteProtype2 extends Prototype {
public ConcreteProtype2(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcreteProtype2(this.getId());
return prototype;
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
ConcreteProtype1 protype1 = new ConcreteProtype1("Protype1");
ConcreteProtype1 protypeCopy1 = (ConcreteProtype1)protype1.clone();
System.out.println(protypeCopy1.getId());
System.err.println(protype1.toString());
System.err.println(protypeCopy1.toString());
ConcreteProtype2 protype2 = new ConcreteProtype2("Protype2");
ConcreteProtype2 protypeCopy2 = (ConcreteProtype2)protype2.clone();
System.out.println(protypeCopy2.getId());
System.err.println(protype2.toString());
System.err.println(protypeCopy2.toString());
}
}
/**
* 输出的结果
*/
Protype1
com.sschen.prototype.ConcreteProtype1@2a139a55
com.sschen.prototype.ConcreteProtype1@15db9742
Protype2
com.sschen.prototype.ConcreteProtype2@6d06d69c
com.sschen.prototype.ConcreteProtype2@7852e922
原型模式适合应用场景
- 如果 你 需 要 复 制 一 些 对 象 \color{Blue}{你需要复制一些对象} 你需要复制一些对象,同时又希望 代 码 独 立 于 这 些 对 象 所 属 的 具 体 类 \color{Blue}{代码独立于这些对象所属的具体类} 代码独立于这些对象所属的具体类,可以使用原型模式
- 如果 子 类 的 区 别 仅 在 于 其 他 对 象 的 初 始 化 方 式 \color{RubineRed}{子类的区别仅在于其他对象的初始化方式} 子类的区别仅在于其他对象的初始化方式,那么你可以使用该模式来减少子类的数量,别人创建这些子类的目的可能为了创建特定类型的对象。
原型模式的优缺点
优点:
- 你可以克隆对象, 而无需与它们所属的具体类相耦合。
- 你可以克隆预生成原型, 避免反复运行初始化代码。
- 你可以更方便地生成复杂对象。
- 你可以用继承以外的方式来处理复杂对象的不同配置。
缺点:
克隆包含循环引用的复杂对象可能会非常麻烦。
单例模式
单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
-
图例:
-
详解:
保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源 (例如数据库或文件) 的访问权限
- 运作方式是这样的:如果你创建了一个对象, 同时过一会儿后 你 决 定 再 创 建 一 个 新 对 象 \color{Blue}{你决定再创建一个新对象} 你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而 不 是 一 个 新 对 象 。 \color{RubineRed}{而不是一个新对象。} 而不是一个新对象。
-
单例模式结构
- 代码实现
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
/**
* 输出结果
*/
Hello World!
单例模式之 ---->>> 懒汉式,线程不安全
这种方式是最基本的实现方式,这种实现最大的问题
就
是
不
支
持
多
线
程
\color{RubineRed}{就是不支持多线程}
就是不支持多线程。因为没有加锁
s
y
n
c
h
r
o
n
i
z
e
d
\color{Red}{synchronized}
synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
代码实现
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
单例模式之 ---->>> 懒汉式,线程安全
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步
- 优点:第一次调用才初始化,避免内存浪费。
- 缺点: 必 须 加 锁 s y n c h r o n i z e d \color{Red}{必须加锁synchronized} 必须加锁synchronized才能保证单例,但加锁会影响效率。getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)
代码实现
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
单例模式之 ---->>>饿汉式
这种方式比较常用,但容易产生垃圾对象。
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。
代码实现
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
单例模式适合应用场景
- 如果程序中的某个类对于所有客服端 只 有 一 个 可 用 的 实 例 \color{Red}{只有一个可用的实例} 只有一个可用的实例,可以使用单例模式
- 如果你需要更加严格地 控 制 全 局 变 量 \color{Red}{控制全局变量} 控制全局变量,可以使用单例模式
单例模式的优缺点
优点:
- 你可以保证一个类只有一个实例。
- 你获得了一个指向该实例的全局访问节点。
- 仅在首次请求单例对象时对其进行初始化。
缺点:
- 违反了单一职责原则。 该模式同时解决了两个问题。
- 单例模式可能掩盖不良设计, 比如程序各组件之间相互了解过多等。
- 该模式在多线程环境下需要进行特殊处理, 避免多个线程多次创建单例对象。
- 单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。
适配器模式
适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eIDh8Ge4-1653183048685)(typora-user-images/adapter-zh.png)]
-
详解:
适配器模式通过 封 装 对 象 \color{Red}{封装对象} 封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至察觉不到适配器的存在。 例如, 你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。 -
运作方式:
- 适配器实现与其中一个现有对象兼容的接口。
- 现有对象可以使用该接口安全地调用适配器方法。
- 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。
-
遇到的问题:
假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。
在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R2WSnDvQ-1653183048685)(typora-user-images/problem-zh.png)]
-
解决方案:
为了解决数据格式不兼容的问题, 你可以为分析函数库中的每个类创建将 XML 转换为 JSON 格式的适配器, 然后让客户端仅通过这些适配器来与函数库进行交流。 当某个适配器被调用时, 它会将传入的 XML 数据转换为 JSON 结构, 并将其 传 递 给 被 封 装 分 析 对 象 的 相 应 方 法 \color{Red}{传递给被封装分析对象的相应方法} 传递给被封装分析对象的相应方法。
-
结构
对象适配器:
实现时使用了构成原则: 适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 所有流行的编程语言都可以实现适配器。
类适配器
这一实现使用了继承机制: 适配器同时继承两个对象的接口。 请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++。
-
代码实现:
/** * 适配器模式的意图是将一个已存在的类/接口进行复用,将其转换/具体化成客户希望的另外的一个类/接口。 * 如何实例复用: * 将要进行复用的类,放到目标类的构造方法中,进行实例化,然后在目标类的相应方法中,进行调用,修改原来方法 * 中的参数,或添加相应的逻辑。即复用了已有类的原来方法。 */ public class Test3 { public static void main(String args[]){ //创建一个电源适配器 VoltageAdapter voltageAdapter = new VoltageAdapter(); //创建一个手机 Mobile mobile = new Mobile(voltageAdapter); //充电 mobile.charge(); } } //定义一个手机类 class Mobile{ public static final int V=220; //定义接入手机的电压为220伏 //需要进入电源适配器 否则手机就炸了 private VoltageAdapter voltageAdapter; public Mobile(VoltageAdapter voltageAdapter) { this.voltageAdapter = voltageAdapter; } //手机充电 public void charge(){ //调用适配器方法以替换手机的充电方法 voltageAdapter.StepDownCharge(); } } //电源适配器 --降压 class VoltageAdapter{ //降压充电 --额定充电 public void StepDownCharge(){ System.out.println("准备充电..."); System.out.println("原始电压:" + Mobile.V + "V"); System.out.println("经过变压器转换之后的电压:" + (Mobile.V - 200) + "V"); System.out.println("快速充电中..."); } }
适配器模式适合应用场景
-
当你希望使用某个类,但是 其 接 口 与 其 他 代 码 不 兼 容 时 \color{Red}{其接口与其他代码不兼容时} 其接口与其他代码不兼容时,可以使用适配器类
-
如果你需要 复 用 这 样 一 些 类 \color{Red}{复用这样一些类} 复用这样一些类,他们处于同一个继承体系,并且他们又有了额外的一些共同方法,但是这些共同
方法不是所有在这一继承体系中的子类所具有的共性
适配器模式优缺点
优点:
- _单一职责原则_你可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
缺点:
代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。