设计模式深入解析与实例应用

目录

工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳实践,旨在将对象的创建过程与使用过程分离,以提高代码的灵活性、可维护性和可扩展性。工厂模式主要有以下三种类型:

简单工厂模式 (Simple Factory):

简单工厂不是一个GoF(设计模式的四人帮, Gang of Four)正式提出的设计模式,但它常被认为是工厂方法模式的简化版本。

提供一个统一的接口用于创建相关或依赖对象,而不需要暴露创建逻辑给客户端,也不需要客户端知道具体产品的类。

适用于产品种类不多,且不会频繁新增产品种类的情况。

工厂方法模式 (Factory Method):

属于GoF提出的23种设计模式之一,属于创建型模式。

定义了一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

提高了系统的扩展性,当需要添加新产品时,只需要添加相应的产品类和对应的工厂子类即可,无需修改现有代码。

抽象工厂模式

同样是GoF设计模式之一,也是创建型模式。

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

适用于系统需要独立于产品的创建、组合和表示的情况下,强调的是"系列"对象的创建,而不是单一产品的创建。

这三种模式从简单到复杂,分别解决了不同复杂度的创建需求,提高了软件的结构灵活性和可维护性。在选择使用哪种工厂模式时,应根据项目的具体需求和未来可能的变化来决定。

1.简单工厂模式

优点 :

  1. 代码解耦:客户端不需要知道具体产品的创建细节,降低了模块间的耦合度。
  2. 易于使用:客户端只需调用一个方法就可以创建对象,使得对象的创建更加简单直观。

缺点:

  1. 违背开闭原则:一旦需要添加新产品,就需要修改工厂类的代码,这增加了系统的复杂性和维护难度。
  2. 不灵活:对于产品类型较多的情况,工厂类可能会变得庞大且难以管理。
    难以支持多态:由于工厂类集中创建所有产品,难以利用多态来减少代码重复。

简单工厂模式示例

java 复制代码
// 产品接口
interface Product {
    void show();
}

// 具体产品A
class ConcreteProductA implements Product {
    @Override
    public void show() {
        System.out.println("ConcreteProductA");
    }
}

// 具体产品B
class ConcreteProductB implements Product {
    @Override
    public void show() {
        System.out.println("ConcreteProductB");
    }
}

// 简单工厂
class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        }
        throw new IllegalArgumentException("Invalid type");
    }
}

public class SimpleFactoryDemo {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        productA.show();

        Product productB = SimpleFactory.createProduct("B");
        productB.show();
    }
}

2.工厂方法模式

优点:

  1. 遵循开闭原则:新增产品时,只需要增加对应的产品类和工厂子类,无需修改已有代码。
  2. 高内聚低耦合:每个工厂子类负责一种产品的创建,职责清晰,易于维护和扩展。
  3. 支持多态:通过面向接口编程,可以充分利用多态特性,提高代码的灵活性。

缺点:

  1. 类膨胀:随着产品种类的增加,需要创建大量的工厂类和产品类,可能会导致类的数量急剧增加。
  2. 客户需要知道具体工厂:客户端需要决定使用哪个工厂来创建产品,这在某些情况下可能降低代码的透明度。

工厂方法模式示例

java 复制代码
// 抽象工厂
interface Factory {
    Product createProduct();
}

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

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

public class FactoryMethodDemo {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.show();

        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.show();
    }
}

3.抽象工厂模式

优点:

  1. 系列产品的创建:非常适合创建一系列相关或相互依赖的对象,保持产品族内的产品一致性。
  2. 高度封装:通过抽象工厂,可以隐藏具体产品的创建细节,提供一个高层的接口给客户端使用。
  3. 易于扩展:增加新的产品族时,只需要增加一个新的抽象工厂类和具体工厂类,不影响现有的客户代码。

缺点:

  1. 复杂度提高:抽象工厂模式的结构较为复杂,理解和维护成本相对较高。
  2. 产品族扩展困难:一旦定义好抽象工厂和产品族,向产品族中添加新的产品比较困难,可能需要修改抽象工厂的接口。
    抽象工厂模式示例
    假设我们有两个产品等级结构:操作系统和浏览器。
java 复制代码
// 抽象产品:操作系统
interface OperatingSystem {
    void showOS();
}

// 具体产品:Windows操作系统
class Windows implements OperatingSystem {
    @Override
    public void showOS() {
        System.out.println("Windows OS");
    }
}

// 具体产品:Mac操作系统
class Mac implements OperatingSystem {
    @Override
    public void showOS() {
        System.out.println("Mac OS");
    }
}

// 抽象产品:浏览器
interface Browser {
    void showBrowser();
}

// 具体产品:Chrome浏览器
class Chrome implements Browser {
    @Override
    public void showBrowser() {
        System.out.println("Chrome");
    }
}

// 具体产品:Safari浏览器
class Safari implements Browser {
    @Override
    public void showBrowser() {
        System.out.println("Safari");
    }
}

// 抽象工厂
interface AbstractFactory {
    OperatingSystem createOperatingSystem();
    Browser createBrowser();
}

// 具体工厂:Windows产品族
class WindowsFactory implements AbstractFactory {
    @Override
    public OperatingSystem createOperatingSystem() {
        return new Windows();
    }

    @Override
    public Browser createBrowser() {
        return new Chrome();
    }
}

// 具体工厂:Mac产品族
class MacFactory implements AbstractFactory {
    @Override
    public OperatingSystem createOperatingSystem() {
        return new Mac();
    }

    @Override
    public Browser createBrowser() {
        return new Safari();
    }
}

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        AbstractFactory windowsFactory = new WindowsFactory();
        windowsFactory.createOperatingSystem().showOS();
        windowsFactory.createBrowser().showBrowser();

        AbstractFactory macFactory = new MacFactory();
        macFactory.createOperatingSystem().showOS();
        macFactory.createBrowser().showBrowser();
    }
}

每种工厂模式都有其适用场景和局限性,选择合适的模式需基于项目的需求、扩展性和维护性等因素综合考虑。

策略模式

策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户,使代码结构更加灵活,易于扩展。
优点

  1. 算法的封装与互换:策略模式将算法封装在独立的类中,使得算法的变更不会影响到使用算法的客户类,同时也方便在运行时切换算法。
  2. 遵循开闭原则:新增策略时,只需要增加新的策略类,不需要修改原有代码,增强了系统的可扩展性。
    简化复杂条件判断:可以消除大量使用条件语句(如if-else或switch-case)来选择算法的代码,使得代码更加清晰和易于维护。
  3. 提高代码复用性:相同的行为可以被多个上下文重用,减少了代码冗余。

缺点

  1. 客户端必须了解策略:客户端需要知道所有策略类的存在,并决定使用哪个策略,这可能会增加客户端的复杂度。

  2. 类数量增加:每增加一个策略,就需要增加一个类,可能导致类的数量膨胀。

  3. 过度设计风险:对于简单的场景,使用策略模式可能会导致设计过度复杂。

代码示例

假设我们有一个购物车系统,需要根据不同的支付策略计算订单的总金额,比如正常价格、会员折扣价、促销价等。

java 复制代码
// 策略接口
interface PaymentStrategy {
    double calculateTotal(double amount);
}

// 具体策略类:正常支付
class NormalPayment implements PaymentStrategy {
    @Override
    public double calculateTotal(double amount) {
        return amount;
    }
}

// 具体策略类:会员折扣支付
class MemberDiscountPayment implements PaymentStrategy {
    @Override
    public double calculateTotal(double amount) {
        return amount * 0.9; // 会员享有10%折扣
    }
}

// 具体策略类:促销支付
class PromotionPayment implements PaymentStrategy {
    @Override
    public double calculateTotal(double amount) {
        return amount * 0.8; // 促销活动打8折
    }
}

// 环境类(Context),使用策略的类
class ShoppingCart {
    private PaymentStrategy strategy;

    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public double calculateTotal(double amount) {
        return strategy.calculateTotal(amount);
    }
}

public class StrategyPatternDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        // 使用会员折扣策略
        cart.setPaymentStrategy(new MemberDiscountPayment());
        System.out.println("会员折扣总价:" + cart.calculateTotal(100));

        // 切换到促销策略
        cart.setPaymentStrategy(new PromotionPayment());
        System.out.println("促销活动总价:" + cart.calculateTotal(100));

        // 使用正常支付策略
        cart.setPaymentStrategy(new NormalPayment());
        System.out.println("正常支付总价:" + cart.calculateTotal(100));
    }
}

责任链模式概述

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许将请求沿着处理者链进行传递,直到找到能够处理该请求的处理者。每个处理者都包含对下个处理者的引用,如果一个处理者不能处理请求,则会将请求传递给链中的下一个处理者。这样,多个对象都有机会处理请求,从而避免了请求发送者与接收者之间的耦合。
优点

  1. 解耦:发送请求的对象与处理请求的对象解耦,两者不直接关联,易于扩展和维护。
  2. 简化对象:每个处理者只需关注自己的处理逻辑,不需要知道完整的请求处理链。
  3. 灵活性:可以通过添加新的处理者或者改变处理者的顺序来动态地调整处理流程,而无需修改已有代码。

缺点

  1. 不能保证请求被处理:如果链中的所有处理者都不处理请求,请求可能会被忽略。
  2. 调试困难:因为请求的处理路径可能较为复杂,定位问题时可能需要跟踪整个链。
  3. 性能问题:链过长时,请求的处理可能会涉及多个对象,影响系统性能。
  4. 职责链构建复杂性:客户端需要正确构建职责链,不当的配置可能导致循环调用或请求丢失。

代码示例

假设我们有一个简单的审批系统,需要根据不同的金额级别由不同级别的经理进行审批。

java 复制代码
// 定义处理请求的接口
interface Approver {
    void setNextApprover(Approver nextApprover);
    void processRequest(int amount);
}

// 具体处理者:部门经理
class DepartmentManager implements Approver {
    private Approver nextApprover;

    @Override
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }

    @Override
    public void processRequest(int amount) {
        if (amount <= 5000) {
            System.out.println("部门经理批准了 " + amount + " 元的请求。");
        } else if (nextApprover != null) {
            nextApprover.processRequest(amount);
        }
    }
}

// 具体处理者:总经理
class GeneralManager implements Approver {
    private Approver nextApprover;

    @Override
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }

    @Override
    public void processRequest(int amount) {
        if (amount <= 10000) {
            System.out.println("总经理批准了 " + amount + " 元的请求。");
        } else if (nextApprover != null) {
            nextApprover.processRequest(amount);
        }
    }
}

// 客户端代码
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        Approver departmentManager = new DepartmentManager();
        Approver generalManager = new GeneralManager();

        // 构建责任链
        departmentManager.setNextApprover(generalManager);

        // 提交请求
        departmentManager.processRequest(3000);  // 部门经理处理
        departmentManager.processRequest(8000);  // 总经理处理
        departmentManager.processRequest(12000); // 未处理,超出了所有处理者的权限
    }
}

模板方法模式概述

模板方法模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以重新定义算法的某些步骤,而不改变算法的结构。
优点

  1. 代码复用:通过将不变部分的代码放在父类的模板方法中,减少代码重复。
  2. 易于扩展:子类可以覆盖父类中的抽象方法,以适应不同的需求,增加新的行为。
  3. 行为控制:父类中的模板方法控制了算法的整体流程,子类则专注于实现细节,有助于保持代码结构清晰。

缺点

  1. 类膨胀:如果算法中有很多可变步骤,会导致子类数量增加,系统复杂度可能会上升。
  2. 难以理解:对于新加入项目的成员来说,理解模板方法模式的结构和意图可能需要更多时间。
  3. 过度设计:对于简单的操作,使用模板方法模式可能会造成不必要的复杂度。
    代码举例
    正在开发一个文件处理框架,该框架需要支持多种文件格式(如TXT、PDF、JPEG)的读取和保存操作。虽然每种文件的读写逻辑不同,但整体流程(打开文件、读取数据、处理数据、保存数据、关闭文件)是相似的。
java 复制代码
// 抽象类,定义文件处理的模板方法
abstract class FileProcessor {
    public final void processFile(String filePath) {
        openFile(filePath);
        readFile();
        processData();
        writeFile();
        closeFile();
    }

    abstract void openFile(String filePath); // 抽象方法,由子类实现打开文件
    abstract void readFile(); // 抽象方法,由子类实现读取文件内容
    abstract void processData(); // 抽象方法,由子类实现数据处理逻辑
    abstract void writeFile(); // 抽象方法,由子类实现写入文件
    abstract void closeFile(); // 抽象方法,由子类实现关闭文件
}

// 具体实现类,处理TXT文件
class TXTFileProcessor extends FileProcessor {
    @Override
    void openFile(String filePath) {
        System.out.println("Opening TXT file: " + filePath);
    }

    @Override
    void readFile() {
        System.out.println("Reading TXT content");
    }

    @Override
    void processData() {
        System.out.println("Processing TXT data");
    }

    @Override
    void writeFile() {
        System.out.println("Writing processed TXT data back to file");
    }

    @Override
    void closeFile() {
        System.out.println("Closing TXT file");
    }
}

// 具体实现类,处理PDF文件
class PDFFileProcessor extends FileProcessor {
    @Override
    void openFile(String filePath) {
        System.out.println("Opening PDF file: " + filePath);
    }

    @Override
    void readFile() {
        System.out.println("Reading PDF content");
    }

    @Override
    void processData() {
        System.out.println("Processing PDF data");
    }

    @Override
    void writeFile() {
        System.out.println("Writing processed PDF data back to file");
    }

    @Override
    void closeFile() {
        System.out.println("Closing PDF file");
    }
}

public class FileProcessingDemo {
    public static void main(String[] args) {
        FileProcessor txtProcessor = new TXTFileProcessor();
        FileProcessor pdfProcessor = new PDFFileProcessor();

        txtProcessor.processFile("sample.txt"); // 处理TXT文件
        pdfProcessor.processFile("document.pdf"); // 处理PDF文件
    }
}

在这个例子中,FileProcessor类定义了处理文件的模板方法,包括打开文件、读取文件、处理数据、写入文件和关闭文件的一系列步骤。TXTFileProcessor和PDFFileProcessor类作为具体实现,分别覆盖了这些抽象方法以实现特定文件格式的处理逻辑。这种设计使得新增其他文件类型的支持变得非常简单,只需要创建一个新的子类并实现相应的抽象方法即可。

单例模式概述

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类在整个应用程序中仅有一个实例,并提供一个全局访问点来获取这个实例。这有助于控制共享资源的访问,比如数据库连接池、日志系统等。
优点

  1. 控制资源访问:限制对昂贵资源的访问,例如数据库连接或线程池,以避免资源浪费。
  2. 全局唯一性:保证系统中某些服务或资源的唯一性,减少因多个实例导致的潜在冲突。
  3. 简化编程模型:对于需要全局访问的对象,开发者无需关心实例化细节,直接使用即可。

缺点

  1. 违反单一职责原则:单例类除了管理自己的状态外,还负责控制自己的创建过程,职责过多。
  2. 测试困难:由于单例的全局状态,测试时难以模拟不同的环境或状态。
  3. 扩展性问题:在需要多个实例或不同配置的场景下,单例模式不够灵活。
  4. 并发问题:在多线程环境下,如果没有正确实现线程安全,可能导致创建多个实例。

代码示例 (Java)

以下是一个线程安全的懒汉式单例模式实现:

java 复制代码
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;
    }
}

在这个例子中,Singleton类的构造方法是私有的,防止外部直接实例化。getInstance方法提供了获取单例实例的途径,并使用双重检查锁定(double-checked locking)机制来确保线程安全和性能,即只有在实例为null且需要创建时才进行同步操作。这样既保证了单例的唯一性,又尽可能减少了同步带来的性能开销.

相关推荐
诸葛悠闲26 分钟前
设计模式——桥接模式
设计模式·桥接模式
Am心若依旧40928 分钟前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
明月看潮生30 分钟前
青少年编程与数学 02-004 Go语言Web编程 20课题、单元测试
开发语言·青少年编程·单元测试·编程与数学·goweb
Yan.love36 分钟前
开发场景中Java 集合的最佳选择
java·数据结构·链表
椰椰椰耶39 分钟前
【文档搜索引擎】搜索模块的完整实现
java·搜索引擎
大G哥40 分钟前
java提高正则处理效率
java·开发语言
VBA63371 小时前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~1 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
小_太_阳1 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
向宇it1 小时前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎