适配器模式(Adapter Pattern)
适配器模式是一种结构型设计模式,用于将一个类的接口转换为客户端所期望的另一种接口,以便可以使用不兼容的类协同工作。
适配器模式包含三个核心角色:
- 目标接口(Target):客户端所期望的接口。
- 源接口(Adaptee):需要被转换的接口。
- 适配器(Adapter):将源接口转换成目标接口的类。
电压转换的例子:
1 public class Adaptee { 2 3 public int output220v() { 4 return 220; 5 } 6 7 } 8 9 public interface Target { 10 11 int output5v(); 12 13 } 14 15 16 public class Adapter implements Target { 17 private Adaptee adaptee; 18 19 public Adapter(Adaptee adaptee) { 20 this.adaptee = adaptee; 21 } 22 23 @Override 24 public int output5v() { 25 int i = adaptee.output220v(); 26 System.out.println(String.format("原始电压:%d v -> 输出电压:%d v", i, 5)); 27 return 5; 28 } 29 30 } 31 32 33 public class UseAdapter { 34 35 public static void main(String[] args) { 36 Adaptee adaptee = new Adaptee(); 37 Target target = new Adapter(adaptee); 38 target.output5v(); 39 } 40 41 }
解决的问题:适配器模式主要解决两个不兼容接口之间的转换问题。
例如,一个系统需要使用一个新的日志框架,但是原有的代码使用的是旧的日志框架,这时可以使用适配器模式。可以创建一个适配器类,该类实现新的日志框架的接口,并在其内部调用旧的日志框架的接口。这样,原有的代码就可以使用新的日志框架了,而不需要修改原有的代码。
第一,确定目标接口
系统原来的日志接口如下
1 public interface LogFactory { 2 void debug(String tag,String message); 3 }
第二,三方库接口及实现
下面是第三方库提供的日志功能,但是其接口与二狗他们系统目前使用的不兼容。
1 public interface NbLogger { 2 void d(int priority, String message, Object ... obj); 3 } 4 //具体提供日志功能的实现类 5 public class NbLoggerImp implements NbLogger { 6 @Override 7 public void d(int priority, String message, Object... obj) { 8 System.out.println(String.format("牛逼logger记录:%s",message)); 9 } 10 }
第三,构建适配器类
这个类是适配器模式的核心,通过此类就可以将三方库提供的接口转换为系统的目标接口
1 public class LogAdapter implements LogFactory { 2 private NbLogger nbLogger; 3 4 public LogAdapter(NbLogger nbLogger) { 5 this.nbLogger = nbLogger; 6 } 7 8 @Override 9 public void debug(String tag, String message) { 10 Objects.requireNonNull(nbLogger); 11 nbLogger.d(1, message); 12 } 13 }
LogAdapter
实现了系统的目标接口,同时持有三方库NbLogger
的引用。
第四,客户端使用
public class AdapterClient { public void recordLog() { LogFactory logFactory = new LogAdapter(new NbLoggerImp()); logFactory.debug("Test", "我将使用牛逼logger打印log"); } }
可以看到,通过适配器客户端就可以很轻松的切换到新的日志系统了。
桥接模式(Bridge Pattern)
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分和实现部分分离开来,以便两者可以独立地变化。桥接模式的核心思想是将一个系统分成多个维度,每个维度单独考虑,从而简化系统,提高可扩展性和可维护性。
桥接模式所涉及的角色有:
- Abstraction:抽象类
- RefinedAbstraction:扩充抽象类
- Implementor:实现类接口
- ConcreteImplementor:具体实现类
举个例子,假设有一个形状接口 Shape 和一个颜色接口 Color,如果需要实现各种形状和各种颜色的组合,传统的做法是定义大量的子类,如 RedCircle、GreenCircle、BlueCircle、RedRectangle、GreenRectangle、BlueRectangle 等等。而使用桥接模式,可以把形状和颜色分别抽象出来,分别作为抽象类 Shape 和 Color 的实现类,然后将它们组合起来即可,如 RedCircle、GreenRectangle 等等。这样,在新增一种形状或颜色时,只需要新增一个实现类即可,不需要修改现有的代码,从而提高了代码的可扩展性和维护性。这里是一个简单的 Java 代码示例,演示了如何使用桥接模式来实现 Shape 和 Color 的组合,而不需要定义大量的子类:
1 // 定义颜色接口 2 public interface Color { 3 void fill(); 4 } 5 6 // 实现 Red Color 7 public class Red implements Color { 8 9 @Override 10 public void fill() { 11 System.out.println("Filling with red color..."); 12 } 13 } 14 15 // 实现 Green Color 16 public class Green implements Color { 17 18 @Override 19 public void fill() { 20 System.out.println("Filling with green color..."); 21 } 22 } 23 24 // 定义形状抽象类 25 public abstract class Shape { 26 protected Color color; 27 28 public Shape(Color color) { 29 this.color = color; 30 } 31 32 abstract void draw(); 33 } 34 35 // 实现 Circle 形状 36 public class Circle extends Shape { 37 38 public Circle(Color color) { 39 super(color); 40 } 41 42 @Override 43 void draw() { 44 System.out.print("Drawing circle with "); 45 color.fill(); 46 } 47 } 48 49 // 实现 Rectangle 形状 50 public class Rectangle extends Shape { 51 52 public Rectangle(Color color) { 53 super(color); 54 } 55 56 @Override 57 void draw() { 58 System.out.print("Drawing rectangle with "); 59 color.fill(); 60 } 61 } 62 63 // 测试 64 public class BridgePatternDemo { 65 66 public static void main(String[] args) { 67 Color red = new Red(); 68 Shape circle = new Circle(red); 69 circle.draw(); 70 71 Color green = new Green(); 72 Shape rectangle = new Rectangle(green); 73 rectangle.draw(); 74 } 75 }
组合模式(Composite Pattern)
组合模式是一种结构型设计模式,它将对象组织成树形结构,使得客户端可以以一致的方式处理单个对象和对象组合。
组合模式由以下两种对象组成:
- 叶子节点:表示树形结构中的单个对象。
- 组合节点:表示树形结构中的对象组合,它可以包含其他组合节点和叶子节点。
组合模式的核心思想是将对象组织成树形结构,并且让客户端能够以一致的方式处理单个对象和对象组合。这种结构可以使得客户端将组合对象和单个对象一视同仁,从而简化了客户端的代码。此外,组合模式还可以通过递归方式访问整个树形结构,使得我们可以方便地对树形结构中的对象进行操作。
一个常见的例子是文件系统,文件系统由目录和文件两种元素组成,目录可以包含子目录和文件,而文件只能包含数据。
我们可以使用组合模式来构建文件系统的结构。首先定义一个抽象类 AbstractFile
,表示文件和目录的基类,包含了一些基本的操作方法。然后定义 File
类和 Directory
类,分别表示文件和目录。Directory
类维护了一个子节点的列表,可以添加、删除子节点,以及对子节点进行操作。这样就可以通过组合方式来构建出任意复杂的目录结构。
示例代码如下:
1 // 抽象文件类 2 abstract class AbstractFile { 3 public abstract void add(AbstractFile file); 4 public abstract void remove(AbstractFile file); 5 public abstract AbstractFile getChild(int index); 6 public abstract void operation(); 7 } 8 9 // 文件类 10 class File extends AbstractFile { 11 private String name; 12 13 public File(String name) { 14 this.name = name; 15 } 16 17 public void add(AbstractFile file) { 18 System.out.println("不支持该方法"); 19 } 20 21 public void remove(AbstractFile file) { 22 System.out.println("不支持该方法"); 23 } 24 25 public AbstractFile getChild(int index) { 26 System.out.println("不支持该方法"); 27 return null; 28 } 29 30 public void operation() { 31 System.out.println("访问文件 " + name); 32 } 33 } 34 35 // 目录类 36 class Directory extends AbstractFile { 37 private List<AbstractFile> files = new ArrayList<>(); 38 private String name; 39 40 public Directory(String name) { 41 this.name = name; 42 } 43 44 public void add(AbstractFile file) { 45 files.add(file); 46 } 47 48 public void remove(AbstractFile file) { 49 files.remove(file); 50 } 51 52 public AbstractFile getChild(int index) { 53 return files.get(index); 54 } 55 56 public void operation() { 57 System.out.println("访问目录 " + name); 58 for (AbstractFile file : files) { 59 file.operation(); 60 } 61 } 62 }
使用组合模式可以方便地构建出任意复杂的文件系统结构,增加、删除和操作文件和目录也十分简单。