適配器模式
將一個類的接口轉換成客戶端期望的另一個接口,解決接口不兼容問題。
適配器模式由四部分組成:
-
客戶端:即需要使用目標接口的類
-
目標接口
-
需要適配的類,也就是已經存在好的功能,但客戶端通過目標接口沒辦法使用這個類
-
適配器,會實現目標接口,然后耦合需要適配的類,調用類的功能
分為類適配器和對象適配器
// 目標接口
public interface Target {void request();
}
// 適配者類
public class Adaptee {public void specificRequest() {System.out.println("Adaptee's specificRequest");}
}
// 類適配器
public class Adapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Target target = new Adapter();target.request(); // Output: Adaptee's specificRequest}
}
對象適配器: 通過組合,讓適配器類持有現有類的實例,并實現目標接口**。對象適配器使用組合關系來實現接口的適配**,較為常用。
// 目標接口
public interface Target {void request();
}
// 適配者類
public class Adaptee {public void specificRequest() {System.out.println("Adaptee's specificRequest");}
}
// 對象適配器
public class Adapter implements Target { //這里不再是和類適配器一樣繼承適配器,而是在類中裝配適配器對象private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Adaptee adaptee = new Adaptee();Target target = new Adapter(adaptee);target.request(); // Output: Adaptee's specificRequest}
}
Java 日志中的 slf4j 其實就是使用了適配器模式來統一不同日志框架接口,使得我們不需要修改代碼就可以替換不同的底層日志實現。
橋接模式
主要的作用是將抽象和實現解耦,使它們可以獨立地變化。
我們熟知的JDBC 就使用了橋接模式,JDBC定義了抽象的規范,不同的數據庫廠商遵循這些規范,但是它們各自又會有不同的實現。
在使用中,如果我們數據庫是 mysql,則傳入 com.mysql.jdbc.Driver 驅動實現類,如果要換成 oracle替換實現類為oracle.jdbc.driver.OracleDriver 即可,這就是典型的抽象與實現解耦。
代碼實例
實現形狀顏色解耦
-
實現化(Implementor):定義顏色接口
public interface Color {void applyColor(); }
-
具體實現化(Concrete Implementor):實現具體顏色
public class RedColor implements Color {@Overridepublic void applyColor() { System.out.println("Applying red color"); //紅色} }public class BlueColor implements Color { //藍色@Overridepublic void applyColor() {System.out.println("Applying blue color");} }
-
抽象化(Abstraction):定義形狀接口
public abstract class Shape {protected Color color;public Shape(Color color) {this.color = color;}public abstract void draw(); }
-
細化抽象化(Refined Abstraction):實現具體形狀
public class Circle extends Shape {public Circle(Color color) {super(color);}@Overridepublic void draw() {System.out.print("Drawing a circle with ");color.applyColor();} }public class Rectangle extends Shape {public Rectangle(Color color) {super(color);}@Overridepublic void draw() {System.out.print("Drawing a rectangle with ");color.applyColor();} }
-
客戶端代碼
public class Client {public static void main(String[] args) {// 創建紅色圓形Shape redCircle = new Circle(new RedColor());redCircle.draw(); // 輸出: Drawing a circle with Applying red color// 創建藍色矩形Shape blueRectangle = new Rectangle(new BlueColor());blueRectangle.draw(); // 輸出: Drawing a rectangle with Applying blue color} }
組合模式
將對象組合成樹狀結構以表示“整體——部分”的層次關系
以部門為示例,展示組織內的部門和人員信息
// 抽象組織
interface OrganizationComponent { void showDetails();
}// 組織人員
class Employee implements OrganizationComponent {private String name;private String position;public Employee(String name, String position) {this.name = name;this.position = position;}@Overridepublic void showDetails() {System.out.println("Employee: " + name + ", Position: " + position);}
}// 組織部門
class Department implements OrganizationComponent {private String name;private List<OrganizationComponent> components = new ArrayList<>();public Department(String name) {this.name = name;}@Overridepublic void showDetails() {System.out.println("Department: " + name);for (OrganizationComponent component : components) {component.showDetails();}}public void add(OrganizationComponent component) { //往部門添加人員 或 部門components.add(component);}public void remove(OrganizationComponent component) {components.remove(component);}
}// 使用代碼
public class Client {public static void main(String[] args) {// 創建人員OrganizationComponent employee1 = new Employee("John Doe", "Developer");OrganizationComponent employee2 = new Employee("Jane Smith", "Designer");OrganizationComponent employee3 = new Employee("Emily Davis", "Manager");// 創建部門Department engineeringDepartment = new Department("Engineering Department");Department designDepartment = new Department("Design Department");Department headDepartment = new Department("Head Department");// 添加人員到部門engineeringDepartment.add(employee1);designDepartment.add(employee2);headDepartment.add(employee3);// 添加子部門到上級部門headDepartment.add(engineeringDepartment);headDepartment.add(designDepartment);// 顯示整個組織結構的詳情headDepartment.showDetails();// 輸出// Department: Head Department// Employee: Emily Davis, Position: Manager// Department: Engineering Department// Employee: John Doe, Position: Developer// Department: Design Department// Employee: Jane Smith, Position: Designer}
}
通過組合模式,提供統一的接口,簡化對層次結構的處理,使得使用方代碼更加簡潔與靈活。
裝飾器模式
主要作用是通過創建包裝類來實現功能的增強,而不是修改原始類。
通過創建一個裝飾器類,該類實現了與原始對象相同的接口,并持有一個原始對象的引用。通過將原始對象傳遞給裝飾器
代理模式中,代理類附加的是跟原始類無關的功能,而在裝飾器模式中,裝飾器類附加的是跟原始類相關的增強功能。
最典型的裝飾器實現就是 Java 中的I/O類庫,示例代碼如下:
import java.io.*;
public class IOExample {public static void main(String[] args) throws IOException {File file = new File("test.txt");FileInputStream fis = new FileInputStream(file); //讀取文件流BufferedInputStream bis = new BufferedInputStream(fis); //fis被BufferedlnputStream 裝飾,提供了緩存的功能DataInputStream dis = new DataInputStream(bis); //被 DatalnputStream 裝飾,提供了按數據類型讀取的功能while (dis.available() > 0) {System.out.println(dis.readLine());}dis.close();}
}
文件流的代碼比較復雜,這里就不展示了,我們簡單看個多裝飾器疊加的代碼實現:
// 組件
interface Component {void operation();
}// 具體構件
class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("ConcreteComponent operation");}
}// 裝飾器
abstract class Decorator implements Component {protected Component component; public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}
// 具體裝飾器 A
class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedBehavior();}private void addedBehavior() {System.out.println("ConcreteDecoratorA added behavior");}
}
// 具體裝飾器 B
class ConcreteDecoratorB extends Decorator {public ConcreteDecoratorB(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedState();}private void addedState() {System.out.println("ConcreteDecoratorB added state");}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Component component = new ConcreteComponent();Component decoratorA = new ConcreteDecoratorA(component);Component decoratorB = new ConcreteDecoratorB(decoratorA);decoratorB.operation();// Output:// ConcreteComponent operation// ConcreteDecoratorA added behavior// ConcreteDecoratorB added state}
}
外觀模式
也叫門面模式,提供了一個簡化的接口,用于訪問復雜系統中的一組接口。將復雜系統的功能封裝起來,讓客戶端可以更方便地使用系統功能而不需要了解其內部復雜結構。
舉個例子就清晰了。例如 A系統有 a、b、c三個接口,B需要分別調用 a、b、c三個接口,這樣比較麻煩,所以A將 a、b、c封裝成一個接口給B調用,對B來說使用起來就方便了,這就是外觀模式
class CPU { //CPUpublic void start() {System.out.println("CPU 啟動");}public void shutdown() {System.out.println("CPU 關閉");}
}
class Memory { //內存public void start() {System.out.println("內存啟動");}public void shutdown() {System.out.println("內存關閉");}
}class HardDrive { //硬盤public void start() {System.out.println("硬盤啟動");}public void shutdown() {System.out.println("硬盤關閉");}}
class ComputerFacade { //外觀類,封裝了計算機系統的一組復雜接口,包括 CPU、內存和硬盤的啟動和關閉private CPU cpu;private Memory memory;private HardDrive hardDrive;public ComputerFacade() {cpu = new CPU();memory = new Memory();hardDrive = new HardDrive();}public void start() { //啟動計算機System.out.println("計算機啟動開始");cpu.start();memory.start();hardDrive.start();System.out.println("計算機啟動完成");}public void shutdown() { //關閉計算機System.out.println("計算機關閉開始");cpu.shutdown();memory.shutdown();hardDrive.shutdown();System.out.println("計算機關閉完成");}
}
ComputerFacade computerFacade = new ComputerFacade();
computerFacade.start();
// 輸出:
// 計算機啟動開始
// CPU 啟動
// 內存啟動
// 硬盤啟動
// 計算機啟動完成computerFacade.shutdown();
// 輸出:
// 計算機關閉開始
// CPU 關閉
// 內存關閉
// 硬盤關閉
// 計算機關閉完成
享元模式
享元模式本質就是對象池,判斷是否存在,若存在則取,不存在則添加。
Integer的緩存(-128~127)采用了享元模式。
具體原理可看 Java基礎 29.什么是Java的Integer緩沖池?
示例:
假設我們要繪制棋盤上的棋子,棋子的種類有很多,但是每種棋子的形狀和顏色是固定的。我們可以使用享元模式來共享相同種類的棋子對象,從而減少對象的創建數量。
// 棋子接口
interface ChessPiece {void setColor(String color);void display(int x, int y);
}
// 具體棋子類
class ConcreteChessPiece implements ChessPiece {private String color;public ConcreteChessPiece(String color) {this.color = color;}@Overridepublic void setColor(String color) {this.color = color;}@Overridepublic void display(int x, int y) {System.out.println("Chess Piece color: " + color + ", position: (" + x + "," + y + ")");}
}
// 享元工廠類
class ChessPieceFactory {private Map<String, ChessPiece> chessPieces;public ChessPieceFactory() {this.chessPieces = new HashMap<>(); //用集合存chessPieces}public ChessPiece getChessPiece(String color) { //返回單例ChessPiece chessPiece = chessPieces.get(color);if (chessPiece == null) {chessPiece = new ConcreteChessPiece(color);chessPieces.put(color, chessPiece);}return chessPiece;}
}
// 客戶端代碼
public class Client {public static void main(String[] args) {ChessPieceFactory chessPieceFactory = new ChessPieceFactory();ChessPiece blackPiece1 = chessPieceFactory.getChessPiece("black"); ChessPiece blackPiece2 = chessPieceFactory.getChessPiece("black");ChessPiece whitePiece1 = chessPieceFactory.getChessPiece("white");ChessPiece whitePiece2 = chessPieceFactory.getChessPiece("white");blackPiece1.display(1, 2); // 輸出:Chess Piece color: black, position: (1,2)blackPiece2.display(3, 4); // 輸出:Chess Piece color: black, position: (3,4)whitePiece1.display(5, 6); // 輸出:Chess Piece color: white, position: (5,6)whitePiece2.display(7, 8); // 輸出:Chess Piece color: white, position: (7,8)}
}
代理模式
作用:在不改變原始類的情況下,為其提供一個代理,以控制對這個對象的訪問
(代理模式和裝飾器模式很像,其主要不同是,前者是控制原對象的訪問,后者是為原對象增強已有類的功能)
核心是創建一個代理類,該類實現了與原始對象相同的接口,并持有一個原始對象的引用。在代理類的方法中,我們可以添加額外的功能,然后將請求轉發給原始對象進行處理。
// 圖片接口
interface Image {void display();
}
// 具體圖片類
class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadImageFromDisk();}private void loadImageFromDisk() {System.out.println("Loading " + filename + " from disk.");}@Overridepublic void display() {System.out.println("Displaying " + filename);}
}
// 代理圖片類
class ProxyImage implements Image {private RealImage realImage; //引入具體圖片類private String filename; //實現了對原始圖片對象的控制,而不需要修改原始圖片類的代碼public ProxyImage(String filename) {this.filename = filename;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(filename);}beforeDisplay();realImage.display();}private void beforeDisplay() {System.out.println("Before displaying " + filename + ", do some pre-processing.");}
}
// 客戶端代碼
public class Client {public static void main(String[] args) {Image image1 = new ProxyImage("image1.jpg");Image image2 = new ProxyImage("image2.jpg");image1.display(); // 輸出:Before displaying image1.jpg, do some pre-processing.// Loading image1.jpg from disk.// Displaying image1.jpgimage2.display(); // 輸出:Before displaying image2.jpg, do some pre-processing.// Loading image2.jpg from disk.// Displaying image2.jpg}
}