九、设计模型

一、设计模型概述

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
23种设计模型:GoF23
1)创建型模式:
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
2)结构型模式:
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
3)行为型模式:
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式。
4)另一类设计模式:J2EE 设计模式
设计模式七大原则

  • 开闭原则:对扩展开放,对修改关闭
  • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
  • 依赖倒置原则:要面向接口编程,不要面向实现编程。
  • 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
  • 接口隔离原则:要为各个类建立它们需要的专用接口
  • 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话。
  • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

二、工厂模式

工厂模式(Factory Pattern)是一种创建型设计模式,它封装了根据客户端需求创建不同对象实例的逻辑。这种模式提供了一种更加灵活的方式来创建对象,可以让客户端从具体的类实现中解耦出来,并通过统一的接口来创建对象。

在工厂模式中,通常会定义一个抽象工厂接口(Factory),用于定义创建对象的方法。而具体的创建逻辑由具体工厂类(Concrete Factory)来完成,每个具体工厂类能够根据客户端的需求创建相应的对象实例。

一个经典的工厂模式例子是创建不同的数据库连接对象。假设我们有一个数据访问层,需要连接不同类型的数据库,例如MySQL、Oracle和SQL Server等。我们可以使用工厂模式来创建相应的数据库连接对象。

首先,我们定义一个抽象的数据库连接接口,例如DBConnection:

public interface DBConnection {
    void connect();
}

然后,我们定义几个具体的数据库连接实现类,例如MySQLConnection、OracleConnection和SqlServerConnection:

public class MySQLConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接MySQL数据库...");
    }
}

public class OracleConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接Oracle数据库...");
    }
}

public class SqlServerConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接SQL Server数据库...");
    }
}

最后,我们定义一个工厂类DBConnectionFactory,根据客户端的需求创建相应的数据库连接对象实例:

public class DBConnectionFactory {
    public static DBConnection createConnection(String dbType) {
        switch (dbType) {
            case "MySQL":
                return new MySQLConnection();
            case "Oracle":
                return new OracleConnection();
            case "SQL Server":
                return new SqlServerConnection();
            default:
                throw new IllegalArgumentException("Unsupported database type.");
        }
    }
}

客户端代码就可以通过工厂类来创建不同类型的数据库连接对象:

public class DataAccess {
    public void readData() {
        // 通过工厂类创建连接对象
        DBConnection connection = DBConnectionFactory.createConnection("MySQL");
        connection.connect();

        // 读取数据...
    }
}

这个例子中,工厂模式帮助我们根据客户端需求创建不同类型的数据库连接对象实例,通过统一的接口DBConnection来控制对象的创建,并且将具体实现细节封装在工厂类中,使代码更加灵活和可扩展。

三、单例模式

单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供了一个全局的访问方法来访问这个实例。

单例模式的应用场景通常是在需要对某个对象进行全局控制和管理的情况下。例如,在某些场景下,需要确保一个类只有一个实例,例如线程池、配置文件管理器等。

下面是单例模式的一个简单实现示例,使用懒汉式实现:

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

在这个实现中,我们将构造函数设为私有,以防止其他类创建Singleton实例。getInstance() 方法是获取Singleton对象的唯一方式,这个方法中统计检查实例是否存在,如果不存在就创建一个新的实例并返回,否则返回已存在的实例。如果存在多个线程同时调用getInstance()方法,可能会同时触发对instance的创建,导致出现多个实例,这个需要在多线程环境中进行特殊的实现。

总之,单例模式是一种简单而实用的设计模式,能够确保一个类只有一个实例,并提供了全局的访问方式,能够完成全局的控制和管理。同时使用单例模式也需要注意相关线程安全问题。

四、代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过代理对象代替原始对象进行访问控制。代理模式通常使用代理类封装了真实对象,从而可以对真实对象进行访问控制,例如限制对某些对象的复杂或耗时操作的访问等。

代理模式通常包含三个角色:抽象主题(Subject)、真实主题(RealSubject)和代理(Proxy)。抽象主题是定义代理和真实主题的共同接口,通常包括真实主题和代理主题的公共方法;真实主题是实际执行业务逻辑的对象,与代理主题实现同一个接口;代理主题是用于代理真实主题的对象,它们具有与真实主题相同的接口,以便客户端可以使用它们来代替真实主题。

下面是一个简单的代理模式实例,使用代理类限制对真实对象的访问:

// 抽象主题接口
public interface Subject {
    void request();
}

// 真实主题类
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject handles the request.");
    }
}

// 代理类
public class Proxy implements Subject {
    private final RealSubject realSubject;

    public Proxy() {
        this.realSubject = new RealSubject();
    }

    @Override
    public void request() {
        if (checkAccess()) {
            realSubject.request();
            logRequest();
        }
    }

    private boolean checkAccess() {
        System.out.println("Proxy: Checking access prior to invoking a real subject.");
        // TODO: Access checking code goes here.
        return true;
    }

    private void logRequest() {
        System.out.println("Proxy: Logging the time of request.");
        // TODO: Request logging code goes here.
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建代理对象
        Proxy proxy = new Proxy();
        // 通过代理对象调用真实对象的方法
        proxy.request();
    }
}

在这个例子中,抽象主题是Subject接口,定义了代理类和真实主题类的共同接口。真实主题是RealSubject类,它实现了Subject接口,负责完成具体的业务逻辑。代理类是Proxy类,它也实现了Subject接口,用于代理真实主题。在代理类中,我们通过checkAccess()方法来对访问进行控制,并通过logRequest()方法来记录请求日志。在客户端代码中,我们创建代理对象proxy,并通过代理对象来调用真实对象的方法。

总之,代理模式可以通过代理对象来限制对真实对象的访问,并提供额外的功能,例如访问控制、性能监控、缓存等。它可以帮助我们提高系统的可扩展性、可维护性和可测试性,并且可以在不影响现有代码的情况下引入新功能。

五、装饰者模式

装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地向对象添加额外的功能。装饰者模式通常使用组合而非继承的方式来实现这一目的。

在装饰者模式中,有两个主要的角色:抽象组件(Component)和装饰者(Decorator)。抽象组件是一个接口或抽象类,定义了组件的操作方法。具体组件是实现了抽象组件接口的类,它们是我们需要添加额外功能的对象。装饰者也是实现了抽象组件接口的类,它们包含了一个抽象组件类型的成员变量并在其构造函数中初始化该变量。此外,装饰者还会实现与抽象组件接口相同的接口或抽象类,从而能够接受所有与组件相关的操作。装饰者通常会在组件的基础上添加额外的功能或修改组件的行为,同时也会在需要的时候将操作传递给原始组件。

下面是一个简单的装饰模式的例子,我们以咖啡为产品进行装饰:

// 抽象咖啡类
public interface Coffee {
    double getCost(); // 获取咖啡的价格
    String getDescription(); // 获取咖啡的描述
}

// 具体咖啡类
public class Espresso implements Coffee {
    @Override
    public double getCost() {
        return 2.5;
    }

    @Override
    public String getDescription() {
        return "Espresso";
    }
}

// 装饰者类
public class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public double getCost() {
        return coffee.getCost();
    }

    @Override
    public String getDescription() {
        return coffee.getDescription();
    }
}

// 具体装饰者类
public class Milk extends CoffeeDecorator {
    public Milk(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Milk";
    }
}

// 具体装饰者类
public class Sugar extends CoffeeDecorator {
    public Sugar(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Sugar";
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        Coffee espresso = new Espresso();
        System.out.println(espresso.getDescription() + ": $" + espresso.getCost());

        espresso = new Milk(espresso);
        System.out.println(espresso.getDescription() + ": $" + espresso.getCost());

        espresso = new Sugar(espresso);
        System.out.println(espresso.getDescription() + ": $" + espresso.getCost());
    }
}

在这个例子中,抽象咖啡类是Coffee接口,它定义了获取咖啡价格和描述的方法。具体咖啡类是Espresso类,它实现了Coffee接口并表示一种不添加任何额外口味的咖啡。装饰者类是CoffeeDecorator类,它实现了Coffee接口,同时包含了一个Coffee类型的成员变量以及构造函数用于接收该成员变量。具体装饰者类包括了MilkSugar,它们都继承自CoffeeDecorator并添加了额外的功能。在客户端中,我们首先创建了一个Espresso对象,然后使用MilkSugar装饰它。最后,我们通过调用咖啡的getCost()getDescription()方法来计算咖啡的价格和描述。

总之,装饰者模式允许我们动态地添加或修改对象的行为,而无需通过继承来实现。它可以帮助我们遵循开闭原则,即对扩展开放,对修改关闭。装饰者模式的主要优点在于它能够以透明的方式为单个对象添加或修改功能,并且可以很容易地扩展对象的功能。但是它的缺点也很明显,这种灵活性和透明性会导致过多的类和组件的变化,从而使得代码更加复杂,因此需要在使用时仔细权衡利弊。

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值