设计模式浅析(七) ·适配器模式
日常叨逼叨
java设计模式浅析,如果觉得对你有帮助,记得一键三连,谢谢各位观众老爷😁😁
适配器模式
概念
适配器模式将一个类的接口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。它经常用来适配现存的类而无需修改其源码
组成
- 目标接口(Target Interface):这是客户端所期望的接口,也就是客户端真正需要的接口。目标接口定义了客户端想要使用的方法。
- 适配者类(Adaptee Class):这是需要被适配的原始接口。适配者类通常是一个已经存在的类,它包含了一些客户端希望使用的方法,但这些方法的接口与客户端所期望的接口并不一致。
- 适配器类(Adapter Class):这是适配器模式的核心部分。适配器类实现了目标接口,并在其内部调用了适配者类的方法。这样,客户端就可以通过调用适配器类的方法来间接调用适配者类的方法,从而实现了接口转换。
主要实现方式
适配器类通常有三种实现方式:
- 类适配器:通过继承适配者类并实现目标接口来实现。这种方式的缺点是它要求适配者类必须是一个类,而不能是一个接口。此外,由于是通过继承来实现的,因此类适配器只能适配一个适配者类。
- 对象适配器:通过组合适配者类的实例并实现目标接口来实现。这种方式的优点是它不需要继承适配者类,因此可以适配接口或类。此外,对象适配器可以适配多个适配者类,只需要在适配器类中持有多个适配者类的实例即可。
- 接口适配器(默认适配器):当不希望实现接口中的所有方法时,可以创建一个抽象类实现该接口,并为所有方法提供默认实现。子类可以选择性地覆盖这些方法。
案例
在这个案例中,我们将创建一个适配器来将一个旧的硬盘驱动器接口适配到新的硬盘驱动器接口。
类适配器实现
首先,我们定义两个接口,
OldHardDrive
和NewHardDrive
,分别代表旧的硬盘驱动器接口和新的硬盘驱动器接口:
java
//新的硬盘启动器接口
public interface NewHardDrive {
void read();
void write();
void connectToSystem();
}
//旧的硬盘启动器接口
public interface OldHardDrive {
void readData();
void writeData();
void connect();
}
定义旧硬盘驱动器的具体实现
java
public class OldHardDriveImpl implements OldHardDrive {
@Override
public void readData() {
System.out.println("Reading data from old hard drive...");
}
@Override
public void writeData() {
System.out.println("Writing data to old hard drive...");
}
@Override
public void connect() {
System.out.println("Connecting old hard drive...");
}
}
定义适配器
java
// 适配器类,将OldHardDrive接口适配到NewHardDrive接口
public class HardDriveAdapter extends OldHardDriveImpl implements NewHardDrive {
// 由于继承了Adaptee,所以可以直接使用Adaptee的方法
// 同时也需要实现Target接口中的方法
@Override
public void read() {
readData();
}
@Override
public void write() {
writeData();
}
@Override
public void connectToSystem() {
connect();
System.out.println("Adapter connected old hard drive to the new system.");
}
}
最后创建的测试类可以通过类适配器调用目标接口的方法
java
public class Client {
public static void main(String[] args) {
// 创建适配器,将旧硬盘驱动器传递给适配器
NewHardDrive adapter = new HardDriveAdapter();
// 使用适配器调用新接口的方法
adapter.connectToSystem();
adapter.read();
adapter.write();
}
}
在这个例子中,HardDriveAdapter
继承自 OldHardDriveImpl
并实现了 NewHardDrive
接口。当客户端调用 NewHardDrive.connectToSystem()
等方法时,实际上调用的是 HardDriveAdapter
中的 connectToSystem()
等方法,而 connectToSystem()
等方法内部又调用了 OldHardDriveImpl
的 connectToSystem()
等 方法。
类适配器的一个潜在缺点是它依赖于适配者类的具体实现,这违反了里氏替换原则。如果适配者类的接口在未来发生变化,类适配器可能需要进行相应的修改,这可能导致代码的不稳定。
因此,在实际应用中,如果可能的话,更推荐使用对象适配器(Object Adapter),因为它不依赖于适配者类的具体实现,而是通过组合(composition)的方式使用适配者类的实例,从而减少了代码之间的耦合性。
对象适配器实现
还是上述的案例,改为使用对象适配器进行实现
java
//新旧驱动器接口未发生改变
public interface NewHardDrive {
void read();
void write();
void connectToSystem();
}
public interface OldHardDrive {
void readData();
void writeData();
void connect();
}
java
// 旧硬盘驱动器实现也未发生改变
public class OldHardDriveImpl implements OldHardDrive {
@Override
public void readData() {
System.out.println("Reading data from old hard drive...");
}
@Override
public void writeData() {
System.out.println("Writing data to old hard drive...");
}
@Override
public void connect() {
System.out.println("Connecting old hard drive...");
}
}
适配器不再继承旧硬盘驱动器的实现类
java
// 适配器类,将OldHardDrive接口适配到NewHardDrive接口
public class HardDriveAdapter implements NewHardDrive {
private OldHardDrive oldHardDrive;
public HardDriveAdapter(OldHardDrive oldHardDrive) {
this.oldHardDrive = oldHardDrive;
}
@Override
public void read() {
oldHardDrive.readData();
}
@Override
public void write() {
oldHardDrive.writeData();
}
@Override
public void connectToSystem() {
oldHardDrive.connect();
System.out.println("Adapter connected old hard drive to the new system.");
}
}
测试类也发生改变
java
public class Client {
public static void main(String[] args) {
// 创建旧硬盘驱动器实例
OldHardDrive oldHardDrive = new OldHardDriveImpl();
// 创建适配器,将旧硬盘驱动器传递给适配器
NewHardDrive adapter = new HardDriveAdapter(oldHardDrive);
// 使用适配器调用新接口的方法
adapter.connectToSystem();
adapter.read();
adapter.write();
}
}
在这个案例中,HardDriveAdapter
类充当了适配器的角色,它实现了 NewHardDrive
接口,并将旧硬盘驱动器的操作封装在其内部。这样,新系统就可以通过 HardDriveAdapter
类来操作旧硬盘驱动器,而无需关心旧接口的具体实现。这就是适配器模式的一个实际应用案例。
优略对比
类适配器和对象适配器都是适配器模式的不同实现方式,它们各有优缺点,适用于不同的场景。以下是类适配器和对象适配器在Java中的优劣势对比:
类适配器(Class Adapter)
优势:
- 代码简洁性:类适配器通常只需要一个适配器类,该类继承自适配者类并实现目标接口。这种实现方式相对简单和直接。
- 多重继承问题:如果目标接口和适配者类都继承自同一个基类或有相同的接口,那么类适配器就无法工作。在这种情况下,对象适配器是更好的选择。
劣势:
- 继承的局限性:类适配器使用了继承,这意味着适配器类不能同时适配多个不兼容的接口,因为Java不支持多重继承。
- 扩展性问题:如果适配者类的接口发生改变,可能需要修改适配器类的代码,这违背了开放封闭原则(OCP)。
对象适配器(Object Adapter)
优势:
- 灵活性:对象适配器使用对象组合而不是继承,因此可以适配多个不兼容的接口,解决了类适配器中多重继承的问题。
- 更好的遵循OCP:由于使用对象组合,当适配者类的接口发生改变时,只需要修改适配器类的代码,而不需要修改客户端代码,这更符合开放封闭原则。
- 更好的解耦:对象适配器减少了客户端和适配者之间的耦合度,因为客户端只依赖于目标接口,而不是具体的适配者类。
劣势:
- 代码复杂性:与类适配器相比,对象适配器可能需要更多的代码,因为需要创建适配器对象并调用其方法。
- 额外的对象创建开销:由于使用了对象组合,每次需要适配时都需要创建一个新的适配器对象,这可能会增加一些额外的内存开销。
代码相关代码可以参考 代码仓库🌐
ps:本文原创,转载请注明出处