設計模式大全

簡相傑發表於2023-04-05

設計模式

設計模式

設計模式是指在軟體設計中經常遇到的一些重複性問題所提供的可複用解決方案,它可以幫助我們提高軟體的可維護性、可擴充套件性和可重用性。Java中的設計模式是基於物件導向程式設計思想而產生的,它們可以幫助我們更好地組織程式碼並降低程式碼的耦合度。

基本原則

Java中的設計模式基於以下基本原則:

  • 單一職責原則
  • 開放封閉原則
  • 里氏替換原則
  • 依賴倒置原則
  • 介面隔離原則
  • 迪米特法則

常見的設計模式

Java中常見的設計模式包括:

  • 工廠模式
  • 單例模式
  • 原型模式
  • 介面卡模式
  • 裝飾器模式
  • 代理模式
  • 觀察者模式
  • 責任鏈模式
  • 命令模式
  • 模板方法模式
  • 策略模式

設計模式是物件導向程式設計中非常重要的一個概念,它可以幫助我們更好地組織程式碼並提高程式碼的可維護性和可擴充套件性。在實際應用中,我們應該根據需要選擇合適的設計模式來解決問題,避免過度使用設計模式導致程式碼複雜度增加。

基本原則介紹

單一職責原則

單一職責原則(SRP)是指一個類或模組只負責完成一個職責或功能。如果一個類或模組承擔了多個職責或功能,那麼它的職責和功能就會變得不清晰,容易出現bug,並且難以維護和擴充套件。

Java示例程式碼:

public class UserService {
    public void register(User user) {
        // 註冊使用者
    }

    public void login(User user) {
        // 登入使用者
    }

    public void updateUser(User user) {
        // 更新使用者資訊
    }

    public void deleteUser(User user) {
        // 刪除使用者
    }
}

在上面的示例程式碼中,UserService類負責使用者的註冊、登入、更新和刪除這四個職責,這違反了單一職責原則。我們可以將其拆分為四個類,每個類只負責一個職責。

開放封閉原則

開放封閉原則(OCP)是指一個軟體實體應該對擴充套件開放,對修改關閉。也就是說,在不修改原有程式碼的情況下,透過擴充套件它來實現新的功能。

Java示例程式碼:

public interface Shape {
    void draw();
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing rectangle");
    }
}

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing circle");
    }
}

public class ShapeDrawer {
    private List<Shape> shapes = new ArrayList<>();

    public void addShape(Shape shape) {
        shapes.add(shape);
    }

    public void drawShapes() {
        for (Shape shape : shapes) {
            shape.draw();
        }
    }
}

在上面的示例程式碼中,Shape介面表示圖形,RectangleCircle類分別實現了Shape介面。ShapeDrawer類負責繪製圖形,其中addShape方法用於新增圖形,drawShapes方法用於繪製所有圖形。如果我們需要新增一種圖形,只需要建立一個新的類,實現Shape介面,並新增到ShapeDrawer中即可,不需要修改原有程式碼。

里氏替換原則

里氏替換原則(LSP)是指在任何可以使用基類物件的地方,一定可以使用子類物件來替換。也就是說,子類應該能夠替換掉父類並且不會影響程式的正確性。

Java示例程式碼:

public class Vehicle {
    public void run() {
        System.out.println("Vehicle is running");
    }
}

public class Car extends Vehicle {
    @Override
    public void run() {
        System.out.println("Car is running");
    }
}

public class Bike extends Vehicle {
    @Override
    public void run() {
        System.out.println("Bike is running");
    }
}

在上面的示例程式碼中,Vehicle類表示車輛,CarBike類分別繼承自Vehicle類。由於CarBike類繼承自Vehicle類,所以它們可以替換Vehicle類的物件,並且不會影響程式的正確性。

依賴倒置原則

依賴倒置原則(DIP)是指高層模組不應該依賴於低層模組,它們應該依賴於抽象。同時,抽象不應該依賴於具體實現,具體實現應該依賴於抽象。

Java示例程式碼:

public interface MessageSender {
    void send(String message);
}

public class EmailSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

public class SmsSender implements MessageSender {
    @Override
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

public class NotificationService {
    private MessageSender messageSender;

    public NotificationService(MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public void sendNotification(String message) {
        messageSender.send(message);
    }
}

在上面的示例程式碼中,MessageSender介面表示訊息傳送器,EmailSenderSmsSender類分別實現了MessageSender介面。NotificationService類表示通知服務,其中的sendNotification方法可以傳送通知,它透過MessageSender介面與具體的訊息傳送器解耦。這樣,我們可以在不修改NotificationService類的情況下,透過傳遞不同的MessageSender物件來實現不同的訊息傳送方式。

介面隔離原則

介面隔離原則(ISP)是指一個類對另一個類的依賴應該建立在最小的介面上。也就是說,不應該強迫一個類依賴於它不需要的方法。

Java示例程式碼:

public interface Payment {
    void pay();
}

public class CreditCardPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Paying with credit card");
    }
}

public class CashPayment implements Payment {
    @Override
    public void pay() {
        System.out.println("Paying with cash");
    }
}

public class ShoppingCart {
    private List<Payment> payments = new ArrayList<>();

    public void addPayment(Payment payment) {
        payments.add(payment);
    }

    public void checkout() {
        for (Payment payment : payments) {
            payment.pay();
        }
    }
}

在上面的示例程式碼中,Payment介面表示支付方式,CreditCardPaymentCashPayment類分別實現了Payment介面。ShoppingCart類表示購物車,其中的addPayment方法用於新增支付方式,checkout方法用於結賬。由於ShoppingCart類依賴於Payment介面,所以我們可以根據需要新增不同的支付方式,而不需要強迫ShoppingCart類依賴於它不需要的方法。

迪米特法則

迪米特法則(LKP)是指一個物件應該對其他物件有最少的瞭解。也就是說,一個物件應該儘量減少與其他物件之間的互動,只和自己的直接朋友互動。

Java示例程式碼:

public class Teacher {
    private String name;

    public Teacher(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Course {
    private Teacher teacher;

    public Course(Teacher teacher) {
        this.teacher = teacher;
    }

    public void printTeacherName() {
        System.out.println(teacher.getName());
    }
}

public class School {
    private List<Course> courses = new ArrayList<>();

    public void addCourse(Course course) {
        courses.add(course);
    }

    public void printCourseTeacherNames() {
        for (Course course : courses) {
            course.printTeacherName();
        }
    }
}

在上面的示例程式碼中,Teacher類表示教師,Course類表示課程,School類表示學校。由於Course類只依賴於Teacher類,而不依賴於School類,所以我們可以在不修改Course類的情況下,將其新增到School類中,並呼叫printCourseTeacherNames方法列印所有課程的教師名字。

Java中常見的設計模式

工廠模式

工廠模式是一種建立型設計模式,它提供了一種建立物件的方式,而無需暴露物件的建立邏輯。工廠模式有多種實現方式,最常見的有簡單工廠模式、工廠方法模式、抽象工廠模式。

簡單工廠模式

簡單工廠模式是最簡單的工廠模式,它將物件的建立邏輯封裝在一個工廠類中,客戶端只需要呼叫工廠類的靜態方法來獲取物件例項。以下是一個簡單工廠模式的Java程式碼示例:

// 抽象產品類
public interface Product {
    void use();
}

// 具體產品類A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using product A.");
    }
}

// 具體產品類B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using product B.");
    }
}

// 工廠類
public class SimpleFactory {
    public static Product createProduct(String type) {
        switch (type) {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new IllegalArgumentException("Invalid product type.");
        }
    }
}

// 客戶端程式碼
public class Client {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        Product productB = SimpleFactory.createProduct("B");
        productA.use(); // Using product A.
        productB.use(); // Using product B.
    }
}

在這個示例中,工廠類SimpleFactory提供了一個靜態方法createProduct()來建立產品例項,客戶端只需要呼叫這個方法並傳入產品型別引數即可。在createProduct()方法中,根據不同的產品型別返回不同的具體產品例項。

工廠方法模式

工廠方法模式是一種更加靈活的工廠模式,它將物件的建立延遲到子類中實現,客戶端只需要針對抽象工廠和抽象產品程式設計,而無需知道具體的實現類。以下是一個工廠方法模式的Java程式碼示例:

// 抽象產品類
public interface Product {
    void use();
}

// 具體產品類A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using product A.");
    }
}

// 具體產品類B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using product B.");
    }
}

// 抽象工廠類
public interface Factory {
    Product createProduct();
}

// 具體工廠類A
public class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具體工廠類B
public class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客戶端程式碼
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Factory factoryB = new ConcreteFactoryB();
        Product productA = factoryA.createProduct();
        Product productB = factoryB.createProduct();
        productA.use(); // Using product A.
        productB.use(); // Using product B.
    }
}

在這個示例中,抽象工廠類Factory定義了一個抽象方法createProduct(),具體工廠類ConcreteFactoryA和ConcreteFactoryB分別實現了這個方法來建立具體產品例項。客戶端程式碼針對抽象工廠和抽象產品程式設計,透過具體工廠類的例項來建立具體產品例項。

抽象工廠模式

抽象工廠模式是一種提供多個工廠方法的工廠模式,它可以建立一組相關或相互依賴的物件。抽象工廠模式通常需要定義多個抽象產品類和多個抽象工廠類,每個抽象工廠類負責建立一組相關的具體產品。以下是一個抽象工廠模式的Java程式碼示例:

// 抽象產品類Apublic interface ProductA {
    void use();
}

// 具體產品類A1public class ConcreteProductA1 implements ProductA {
    @Override
    public void use() {
        System.out.println("Using product A1.");
    }
}

// 具體產品類A2public class ConcreteProductA2 implements ProductA {
    @Override
    public void use() {
        System.out.println("Using product A2.");
    }
}

// 抽象產品類Bpublic interface ProductB {
    void eat();
}

// 具體產品類B1public class ConcreteProductB1 implements ProductB {
    @Override
    public void eat() {
        System.out.println("Eating product B1.");
    }
}

// 具體產品類B2public class ConcreteProductB2 implements ProductB {
    @Override
    public void eat() {
        System.out.println("Eating product B2.");
    }
}

// 抽象工廠類public interface Factory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具體工廠類1public class ConcreteFactory1 implements Factory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 具體工廠類2public class ConcreteFactory2 implements Factory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

// 客戶端程式碼public class Client {
    public static void main(String[] args) {
        Factory factory1 = new ConcreteFactory1();
        Factory factory2 = new ConcreteFactory2();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        productA1.use();// Using product A1.
        productB1。eat();// Eating product B1.
        productA2.use();// Using product A2.
        productB2.eat();// Eating product B2.
    }
}

在這個示例中,抽象工廠類Factory定義了兩個抽象方法createProductA()和createProductB(),具體工廠類ConcreteFactory1和ConcreteFactory2分別實現了這兩個方法來建立一組相關的具體產品。客戶端程式碼透過具體工廠類的例項來建立具體產品例項,每個工廠類只建立自己對應的產品例項。

單例模式

單例模式是一種建立型設計模式,它保證一個類只有一個例項,並提供了一個全域性訪問點供外部程式碼獲取該例項。單例模式通常需要滿足三個條件:私有化建構函式、提供一個靜態方法獲取例項、保證只有一個例項存在。

懶漢式

以下是一個使用懶漢式實現的單例模式的Java程式碼示例:

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在這個示例中,Singleton類的建構函式是私有的,這意味著外部程式碼不能透過例項化Singleton類來建立新的物件。Singleton類提供了一個靜態方法getInstance()來獲取Singleton類的唯一例項。在getInstance()方法中,如果還沒有建立例項,就會建立一個新例項並返回,否則直接返回已有的例項。

需要注意的是,這種懶漢式實現的單例模式在多執行緒環境下可能會存線上程安全問題。可以透過加鎖或使用雙重檢查鎖定等方式來解決這個問題。

餓漢式

在這種實現方式中,單例物件在類載入的時候就已經建立好了,因此不存線上程安全問題,但是可能會浪費一些記憶體空間。

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

雙重檢查鎖定

這種實現方式透過雙重檢查鎖定來保證執行緒安全和效能,它延遲了物件的建立時間,只有在第一次訪問單例物件時才建立例項。

public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

靜態內部類

這種實現方式透過靜態內部類來延遲單例物件的建立時間,它既保證了執行緒安全,又避免了餓漢式實現方式的記憶體浪費問題。

public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

列舉

這種實現方式利用列舉的特性來保證單例物件的唯一性,它既簡單又安全。

public enum Singleton {
    INSTANCE;
    public void doSomething() {
// ...
    }
}

原型模式

原型模式是一種建立型設計模式,它允許透過複製現有物件來建立新物件,而無需透過例項化來建立物件。原型模式在建立複雜物件的場景下非常有用,可以避免重複建立相同的物件,提高物件建立的效率。

以下是一個原型模式的Java程式碼示例:

public abstract class Prototype implements Cloneable {
    public abstract Prototype clone();
}

public class ConcretePrototype extends Prototype {
    private String field1;
    private int field2;
    public ConcretePrototype(String field1, int field2) {
        this.field1 = field1;
        this.field2 = field2;
    }
    public String getField1() {
        return field1;
    }
    public void setField1(String field1) {
        this.field1 = field1;
    }
    public int getField2() {
        return field2;
    }
    public void setField2(int field2) {
        this.field2 = field2;
    }
    @Override
    public Prototype clone() {
        return new ConcretePrototype(field1, field2);
    }
}

在這個示例中,Prototype是原型類,定義了一個抽象方法clone(),該方法用於複製原型物件。ConcretePrototype是具體原型類,實現了原型類的clone()方法,並定義了自己的屬性和方法。

在工業軟體開發中,原型模式常常用於物件的複製和初始化。例如,在製造業中,產品的製造需要經歷一系列的工藝流程,每個工藝流程都需要對產品進行加工和處理。如果每次都重新建立一個產品物件,那麼就會浪費很多時間和資源。透過原型模式,可以在第一次建立產品物件時,將其複製為原型物件,並在每個工藝流程中複製原型物件,然後對複製後的物件進行加工和處理,這樣就可以避免重複建立物件的問題,提高了加工效率。另外,在設計CAD軟體時,也可以使用原型模式來實現圖形物件的複製和貼上功能。當使用者選擇複製一個圖形物件時,可以將該物件作為原型物件,並將其複製到剪貼簿中。當使用者選擇貼上操作時,可以從剪貼簿中獲取原型物件,並將其複製一份作為新的圖形物件,然後將其插入到畫布中。這樣就可以避免重複建立相同的圖形物件,提高了圖形操作的效率。

介面卡模式

介面卡模式是一種結構型設計模式,它允許不相容的介面之間進行協作。介面卡模式透過建立一箇中間介面卡來轉換一個介面為另一個介面,從而使得原本不相容的介面能夠協同工作。

以下是一個介面卡模式的Java程式碼示例:

public interface Target {
    void request();
}

public class Adaptee {
    public void specificRequest() {
// ...
    }
}

public class Adapter implements Target {
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

在這個示例中,Target是目標介面,定義了一個request()方法。Adaptee是被適配的類,其中包含一個specificRequest()方法。Adapter是介面卡類,它實現了Target介面,幷包含一個Adaptee物件。在Adapter的request()方法中,呼叫Adaptee物件的specificRequest()方法來完成目標介面的請求。

常見的使用場景:

  1. 舊介面與新介面的適配:當系統需要使用一個老介面但是隻有新介面可用時,可以使用介面卡模式將新介面轉換為老介面。這種情況通常發生在系統的升級和改進中。
  2. 外部介面與內部介面的適配:當系統需要使用一個外部介面,但是該介面與內部介面不相容時,可以使用介面卡模式將外部介面轉換為內部介面。這種情況通常發生在系統與第三方庫或服務整合時。
  3. 多個介面之間的適配:當系統需要使用多個不相容的介面進行協同工作時,可以使用介面卡模式將這些介面轉換為統一的介面。這種情況通常發生在系統需要與多個外部系統協同工作時。

總之,介面卡模式可以幫助我們在不改變現有系統介面的情況下,實現不同介面之間的協同工作,提高系統的可擴充套件性和靈活性。

裝飾器模式

裝飾器模式是一種結構型設計模式,允許你透過將物件放入包裝物件中來為原物件新增新的行為。裝飾器模式可以在不修改原始物件的情況下,動態地新增功能。

Java程式碼示例:

// 定義一個介面public interface Shape {
    void draw();
}

// 建立一個實現介面的實體類public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Shape: Rectangle");
    }
}

// 建立一個裝飾器類,實現同樣的介面public abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }

    public void draw(){
        decoratedShape.draw();
    }
}

// 建立擴充套件了 ShapeDecorator 類的實體裝飾類public class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape){
        System.out.println("Border Color: Red");
    }
}

// 使用裝飾器public class DecoratorPatternDemo {
    public static void main(String[] args) {

        Shape rectangle = new Rectangle();

        Shape redRectangle = new RedShapeDecorator(new Rectangle());

        System.out.println("Normal Rectangle:");
        rectangle.draw();

        System.out.println("\nRed Rectangle:");
        redRectangle.draw();
    }
}

在軟體開發中,裝飾器模式通常應用於以下場景:

  • 當需要動態地為物件新增功能或行為時,可以使用裝飾器模式。這種方式比繼承更加靈活,因為它可以在執行時動態地新增或刪除功能。
  • 當不能採用繼承方式對物件進行擴充套件時,可以使用裝飾器模式。例如,當類已經被定義為final時,就無法透過繼承方式進行擴充套件,此時可以使用裝飾器模式來新增新的行為。
  • 當需要在不影響其他物件的情況下,對單個物件進行修改或擴充套件時,可以使用裝飾器模式。這種方式避免了在修改物件時對其他物件造成影響。
  • 在 GUI 程式設計中,裝飾器模式常用於為控制元件新增新的行為或外觀。例如,可以使用裝飾器模式為按鈕新增邊框、背景顏色等效果,而不需要對按鈕本身進行修改。

代理模式

代理模式是一種結構型設計模式,它允許你提供一個代理物件,以控制對其它物件的訪問。

在代理模式中,代理物件擁有與實際物件相同的介面,客戶端無需知道實際物件的存在,只需要與代理物件進行互動。代理物件在需要時將請求轉發給實際物件,並在必要時執行一些額外的邏輯。

下面是一個簡單的Java程式碼示例:

interface Image {
    void display();
}

class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(filename);
    }

    public void display() {
        System.out.println("Displaying " + filename);
    }

    private void loadFromDisk(String filename) {
        System.out.println("Loading " + filename);
    }
}

class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

public class Main {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        image.display();
    }
}

在這個示例中,Image是一個介面,RealImage是實現它的具體類,ProxyImage是實現了Image介面的代理類。

代理類在display()方法中檢查實際物件是否已經被建立,如果沒有,它將建立一個並呼叫其display()方法。如果實際物件已經存在,代理類將直接呼叫它的display()方法。

代理模式在軟體開發中的場景舉例:

  1. 遠端代理:在客戶端和伺服器之間使用遠端代理,客戶端透過遠端代理訪問伺服器上的物件。
  2. 虛擬代理:在必要時,使用虛擬代理來延遲實際物件的建立,以提高效能和減少資源消耗。例如,當需要載入大型影像或影片檔案時,可以使用虛擬代理來避免在初始化時載入所有資料。
  3. 安全代理:在訪問敏感物件時,使用安全代理來控制對物件的訪問。例如,只有經過身份驗證的使用者才能訪問某些物件。
  4. 快取代理:在訪問頻繁的物件時,使用快取代理來快取物件的結果,避免重複計算。例如,在計算複雜數學函式時,可以使用快取代理來儲存已經計算過的結果,以提高效能。
  5. 日誌記錄代理:在訪問物件時,使用日誌記錄代理來記錄訪問日誌,以便後續分析和除錯。例如,在除錯時,可以使用日誌記錄代理來記錄物件的訪問和引數。

觀察者模式

觀察者模式是一種行為型設計模式,它定義了物件之間的一對多依賴關係,當一個物件的狀態發生改變時,所有依賴它的物件都會收到通知並自動更新。

以下是一個觀察者模式的Java程式碼示例:

public interface Observer {
    void update(int data);
}

public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers();
}

public class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int data;
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(data);
        }
    }
    public void setData(int data) {
        this.data = data;
        notifyObservers();
    }
}

public class ConcreteObserver implements Observer {
    private int data;
    @Override
    public void update(int data) {
        this.data = data;
        System.out.println("Observer received data: " + data);
    }
}

在這個示例中,Observer是觀察者介面,定義了一個update()方法。Subject是主題介面,定義了attach()、detach()和notifyObservers()方法。ConcreteSubject是具體主題類,實現了Subject介面,幷包含一個觀察者列表和一個資料欄位。在ConcreteSubject中,當資料發生變化時,透過notifyObservers()方法通知所有的觀察者。ConcreteObserver是具體觀察者類,實現了Observer介面,並在update()方法中列印出接收到的資料。

觀察者模式在軟體開發過程中的場景舉例:

  1. GUI元件

在GUI元件中,使用者介面通常需要根據某些狀態或事件的變化來更新介面。例如,在一個文字編輯器中,當使用者輸入文字時,文字區域和狀態列都需要實時更新。這時,可以使用觀察者模式來實現狀態和介面之間的同步。

  1. 資料庫操作

在資料庫操作中,當資料庫中的某些資料發生變化時,需要及時通知其他系統或元件進行相應的處理。例如,在一個電商網站中,當使用者下單購買商品時,需要更新庫存和訂單資訊。這時,可以使用觀察者模式來實現庫存和訂單之間的同步。

  1. 網路通訊

在網路通訊中,當一個節點的狀態發生變化時,需要及時通知其他節點進行相應的處理。例如,在一個分散式系統中,當一個節點發生故障時,其他節點需要及時接管該節點的任務和資料。這時,可以使用觀察者模式來實現節點之間的同步和協同工作。

總之,觀察者模式可以幫助我們實現物件之間的松耦合和同步,提高系統的可維護性和可擴充套件性。

責任鏈模式

責任鏈模式是一種行為型設計模式,它允許多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。

Java程式碼示例:

// 定義一個抽象處理器public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;

// 責任鏈中的下一個元素protected AbstractLogger nextLogger;

    public void setNextLogger(AbstractLogger nextLogger){
        this.nextLogger = nextLogger;
    }

    public void logMessage(int level, String message){
        if(this.level <= level){
            write(message);
        }
        if(nextLogger !=null){
            nextLogger.logMessage(level, message);
        }
    }

    abstract protected void write(String message);
}

// 建立擴充套件了 AbstractLogger 的實體類public class ConsoleLogger extends AbstractLogger {

    public ConsoleLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("Standard Console::Logger: " + message);
    }
}

public class ErrorLogger extends AbstractLogger {

    public ErrorLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("Error Console::Logger: " + message);
    }
}

public class FileLogger extends AbstractLogger {

    public FileLogger(int level){
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("File::Logger: " + message);
    }
}

// 使用責任鏈模式public class ChainPatternDemo {

    public static void main(String[] args) {
        AbstractLogger loggerChain = getChainOfLoggers();

        loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");

        loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");

        loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
    }

    private static AbstractLogger getChainOfLoggers(){

        AbstractLogger errorLogger = newErrorLogger(AbstractLogger.ERROR);
        AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);

        return errorLogger;
    }
}

場景舉例:

在軟體開發中,責任鏈模式通常應用於以下場景:

  • 當需要將請求傳送給多個物件時,可以使用責任鏈模式。例如,一個異常可以被多個異常處理程式處理,且每個處理程式只負責處理特定型別的異常。
  • 當不確定哪個物件應該處理請求時,可以使用責任鏈模式。例如,一個請求可能需要根據不同的條件由不同的處理程式處理。
  • 當需要動態地新增或刪除處理程式時,可以使用責任鏈模式。這種方式可以讓使用者新增或刪除處理程式,而不需要修改程式碼。
  • 當需要將請求從一個物件傳遞到另一個物件時,可以使用責任鏈模式。例如,在一個分散式系統中,可以使用責任鏈模式將請求從一個節點傳遞到另一個節點,直到找到能夠處理請求的節點為止。

命令模式

命令模式是一種行為型設計模式,它允許你將請求封裝為一個物件,並將其傳遞給不同的物件進行呼叫。

在命令模式中,命令物件封裝了一個請求和相關的引數,可以將其傳遞給呼叫者,呼叫者無需知道請求的具體實現,只需要呼叫命令物件的執行方法即可。

下面是一個簡單的Java程式碼示例:

interface Command {
    void execute();
}

class Light {
    public void turnOn() {
        System.out.println("Light is on");
    }

    public void turnOff() {
        System.out.println("Light is off");
    }
}

class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOn();
    }
}

class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOff();
    }
}

class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

public class Main {
    public static void main(String[] args) {
        Light light = new Light();
        Command lightOnCommand = new LightOnCommand(light);
        Command lightOffCommand = new LightOffCommand(light);

        RemoteControl remoteControl = new RemoteControl();
        remoteControl.setCommand(lightOnCommand);
        remoteControl.pressButton();

        remoteControl.setCommand(lightOffCommand);
        remoteControl.pressButton();
    }
}

在這個示例中,Command是一個介面,LightOnCommandLightOffCommand實現了它。RemoteControl類持有一個Command物件,並在呼叫pressButton()方法時執行該物件的execute()方法。

命令模式在軟體開發中的場景舉例:

  1. 網路請求佇列:在處理網路請求時,可以將請求封裝為命令物件,將其加入到請求佇列中,然後非同步執行。這可以減少網路請求的延遲和提高系統的響應速度。
  2. 撤銷和重做功能:在實現撤銷和重做功能時,可以使用命令模式來記錄操作歷史,每個命令物件都可以記錄其執行前和執行後的狀態,並實現撤銷和重做方法。
  3. 選單系統:在實現選單系統時,可以將每個選單項封裝為命令物件,然後將其新增到選單中。當使用者選擇選單項時,選單物件將呼叫相應的命令物件。
  4. 日誌記錄:在記錄系統操作日誌時,可以使用命令模式來記錄每個操作,包括其執行時間、執行者和執行結果等資訊。
  5. 佇列系統:在實現佇列系統時,可以將每個任務封裝為命令物件,並將其加入到佇列中。佇列系統將非同步執行每個任務,並且可以動態新增或刪除任務。

模板方法

模板方法模式是一種行為型設計模式,它定義了一個演算法的骨架,將演算法中不變的部分抽象出來,而將可變的部分留給子類來實現。模板方法模式使得子類可以在不改變演算法結構的情況下,重新定義演算法的某些步驟。

以下是一個模板方法模式的Java程式碼示例:

public abstract class AbstractClass {
    public final void templateMethod() {
        operation1();
        operation2();
        if (hook()) {
            operation3();
        }
    }
    protected abstract void operation1();
    protected abstract void operation2();
    protected void operation3() {}
    protected boolean hook() {
        return true;
    }
}

public class ConcreteClass1 extends AbstractClass {
    @Override
    protected void operation1() {
        System.out.println("ConcreteClass1: operation1");
    }
    @Override
    protected void operation2() {
        System.out.println("ConcreteClass1: operation2");
    }
}

public class ConcreteClass2 extends AbstractClass {
    @Override
    protected void operation1() {
        System.out.println("ConcreteClass2: operation1");
    }
    @Override
    protected void operation2() {
        System.out.println("ConcreteClass2: operation2");
    }
    @Override
    protected void operation3() {
        System.out.println("ConcreteClass2: operation3");
    }
    @Override
    protected boolean hook() {
        return false;
    }
}

在這個示例中,AbstractClass是抽象模板類,定義了一個templateMethod()方法,其中包含了若干個操作步驟。operation1()和operation2()是抽象方法,需要由子類實現。operation3()是一個可選的鉤子方法,可以由子類覆蓋。hook()是一個鉤子方法,用於控制模板方法中的某些流程。ConcreteClass1和ConcreteClass2是具體子類,實現了抽象方法和鉤子方法。

模板方法模式在軟體開發過程中的場景舉例:

  1. 框架設計

在框架設計中,通常需要定義一些通用的演算法流程,並允許使用者根據自己的需求定製某些具體的演算法步驟。這時,可以使用模板方法模式來定義演算法的骨架,將可變的部分留給使用者來實現。

  1. 測試框架

在測試框架中,通常需要執行一系列的測試用例,並對測試結果進行統計和分析。這時,可以使用模板方法模式來定義測試用例的執行流程,將測試用例的具體實現留給子類來完成。

  1. 資料庫操作

在資料庫操作中,通常需要執行一些通用的資料庫操作步驟,並允許使用者根據自己的需求定製某些具體的操作步驟。這時,可以使用模板方法模式來定義資料庫操作的骨架,將可變的部分留給使用者來實現。

總之,模板方法模式可以幫助我們實現演算法的重用和擴充套件,提高系統的可維護性和可擴充套件性。

策略模式

策略模式是一種行為型設計模式,它允許在執行時選擇演算法的行為。策略模式定義了一系列演算法,將每個演算法封裝起來,並使它們可以相互替換。選擇哪種演算法由客戶端決定。

Java程式碼示例:

// 定義一個介面
public interface Strategy {
    int doOperation(int num1, int num2);
}

// 建立實現介面的實體類
public class OperationAdd implements Strategy{
    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

public class OperationSubtract implements Strategy{
    @Override
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

public class OperationMultiply implements Strategy{
    @Override
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

// 建立 Context 類
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy){
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2){
        return strategy.doOperation(num1, num2);
    }
}

// 使用策略模式
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationSubtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationMultiply());
        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
}

在軟體開發中,策略模式通常應用於以下場景:

  • 當需要在執行時選擇演算法時,可以使用策略模式。例如,在一個遊戲中,玩家可以選擇不同的策略來戰勝不同的敵人,每個敵人可能有不同的弱點和強點。
  • 當需要避免使用大量的條件語句時,可以使用策略模式。例如,在一個電商網站中,可以根據不同的促銷策略來計算折扣,而不需要使用大量的if-else語句。
  • 當需要將演算法的實現與使用演算法的客戶端分離時,可以使用策略模式。這種方式可以使得演算法的實現可以獨立於客戶端進行修改和擴充套件,而不會影響客戶端的程式碼。
  • 當需要在不同的環境下使用不同的演算法時,可以使用策略模式。例如,在一個移動裝置上,可以根據裝置的處理能力來選擇不同的演算法實現。

相關文章