设计模式23种全部
设计模式的七大原则
- 单一职责原则_:_一个类应该只有一个引起它变化的原因。换句话说,一个类应该只有一项职责。这样可以保证类的内聚性,并且降低类之间的耦合性。
- 开闭原则_:_一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。这意味着当需要添加新功能时,应该尽量通过扩展已有代码来实现,而不是修改已有代码。
- 里氏替换原则_:_子类应该能够替换父类并且不影响程序的正确性。这意味着在使用继承时,子类不能修改父类已有的行为,而只能扩展父类的功能。
- 接口隔离原则_:_客户端不应该依赖于它不需要的接口。一个类应该只提供它需要的接口,而不应该强迫客户端依赖于它不需要的接口。
- 依赖倒置原则:高层模块不应该依赖于低层模块,它们都应该依赖于抽象。抽象不应该依赖于具体实现,而具体实现应该依赖于抽象。
- 迪米特法则_:_一个对象应该对其他对象保持最少的了解。换句话说,一个对象只应该与它直接相互作用的对象发生交互,而不应该与其它任何对象发生直接的交互。这样可以降低类之间的耦合性,提高系统的灵活性和可维护性。
- _合成复用原则:_尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
设计模式概况
创建型模式
Java中的创建型设计模式主要用于对象的创建和组装。这些模式通过抽象化和解耦对象的创建过程,可以使系统更加灵活和可扩展。下面是Java中的5种创建型设计模式:
- 单例模式:确保一个类只有一个实例,并提供一个全局访问点。
- 工厂模式:在不暴露创建对象的逻辑的前提下,使用工厂方法来创建对象。
- 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的系列,而不需要指定实际实现类。
- 建造者模式:将复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
- 原型模式: 通过克隆来创建对象,避免了通过new关键字显式调用构造函数的开销。
结构型模式
Java中的结构型设计模式主要用于描述对象之间的关系,包括类和对象的组合、接口和继承等方面。这些模式可以帮助我们更好地组织和管理代码,提高代码的重用性和可维护性。下面是Java中的7种结构型设计模式:
- 适配器模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以一起工作。
- 桥接模式:将抽象部分与它的实现部分分离,以便它们可以独立地变化。
- 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得客户端使用单个对象或者组合对象具有一致性。
- 装饰器模式:动态地给一个对象添加一些额外的职责,就增加功能而言,装饰器模式比生成子类方式更为灵活。
- 外观模式:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
- 享元模式:运用共享技术来有效地支持大量细粒度对象的复用。
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问。
行为型模式
Java中的行为型设计模式主要用于描述对象之间的通信和协作方式,包括算法、责任链、状态等方面。这些模式可以帮助我们更好地组织和管理代码,提高代码的可维护性和可扩展性。下面是Java中的11种行为型设计模式:
- 责任链模式:为解除请求的发送者和接收者之间的耦合,而将请求的处理对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
- 命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
- 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子。
- 迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。
- 中介者模式:用一个中介对象封装一系列的对象交互,使得这些对象不需要显示地相互引用,从而降低耦合度。
- 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
- 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
- 状态模式:允许一个对象在其内部状态发生改变时改变其行为,对象看起来似乎修改了它的类。
- 策略模式:定义一系列的算法,将每个算法封装起来,并使它们之间可以互换。
- 模板方法模式:定义一个操作中的算法骨架,将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。
- 过滤器设计模式:允许在不改变原始对象的情况下,动态地添加或删除对象的行为。
单例设计模式
单例模式是最常用的设计模式之一。它可以确保在整个应用程序中,某个类只有一个实例存在,并提供一种访问这个实例的全局访问点。单例模式在需要限制某些类的实例数量时非常有用。 它通常用于需要全局访问的资源,如配置文件、日志记录器、数据库连接等。
懒汉式
线程不安全
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式
饿汉式在类加载时就创建了单例对象,所以不存在线程安全问题。不过,这种方式可能会导致不必要的资源浪费,因为单例对象的创建可能在应用程序启动时就完成了,而有些应用场景中可能并不需要使用单例对象。
public class Singleton {
// 在类加载时就创建单例对象
private static Singleton instance = new Singleton();
// 将构造函数设为私有,禁止外部创建实例
private Singleton() {}
// 提供获取单例对象的方法
public static Singleton getInstance() {
return instance;
}
}
双重检查锁DCL
它可以在保证线程安全的同时实现延迟加载
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;
}
}
- 第一次判断if的作用是提高效率,对于已经new过对象之后调用
getInstance()
的线程,则不需要再加锁操作。 - 第二次判断if的作用是防止多个线程同时进入第一次if的代码块,从而导致后续获得到锁的线程仍旧new 对象,进而导致instance被多次实例化,别的线程可能在这个过程中请求instance进而导致单例的破坏。
volatile关键字的作用:
- 保证了不同线程对该变量操作的内存可见性。
- 禁止指令重排
instance = new Singleton()
不是个原子性的操作,
- 分配内存空间
- 执行构造方法,初始化对象
- 把这个对象指向这个空间
如果指令重排可能导致132的执行顺序出现,此时当A线程执行到3时,B线程在第一次if判断instance是否为空,结果不为空,直接返回来还未执行构造的对象instance,破坏了单例。
枚举方式
使用枚举实现单例模式的好处是,可以避免反射和序列化攻击。因为枚举类型的构造函数是私有的,所以无法使用反射来创建实例;而且枚举类型的实例在序列化和反序列化时会自动处理好,所以也无法通过序列化和反序列化来破坏单例。
public enum Singleton {
INSTANCE;
public void doSomething() {
// TODO: 实现单例对象的功能
}
}
工厂模式
- 作用:
- 实现了创建者和调用者的分离
- 实例化对象不使用new,用工厂方法替代
- 将选择实现类,创建对象统一管理和控制。从而将调用这跟我们的实现类解耦。
- 实现了创建者和调用者的分离
- 详细分类:
- 简单工厂模式
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工厂称为其他工厂的工厂。
- 简单工厂模式
- OOP七大原则
- 开闭原则:一个软件的实体应当对扩展开放
- 依赖倒置原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
简单工厂模式(静态工厂)
某种程度上不符合设计原则,但实际使用最多。
原始方法
public interface Car {
public void name();
}
public class Tesla implements Car{
public void name(){
System.out.println("特斯拉!");
}
}
public class WuLing implements Car{
public void name(){
System.out.println("五菱宏光!");
}
}
public class Consumer {
public static void main(String[] args) {
// 接口,所有实现类
Car car=new WuLing();
Car car2=new Tesla();
car.name();
car2.name();
// Car car = CarFactory.getCar("五菱");
// car.name();
}
}
运行截图
这种方法需要知道所有接口和实现类才能调用
静态工厂方法
因为CarFactory类中的所有方法都是静态的,需要向其中添加参数来返回不同的对象实例。
增加一个新的产品,如果不修改CarFactory代码就做不到。
public class CarFactory {
// 方法一
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
}else if(car.equals("特斯拉")){
return new Tesla();
}else {
return null;
}
}
// 方法二
public static Car getWuLing(){
return new WuLing();
}
public static Car getTesla(){
return new Tesla();
}
}
public class Consumer {
public static void main(String[] args) {
// 接口,所有实现类
// Car car=new WuLing();
// Car car2=new Tesla();
// car.name();
// car2.name();
Car car = CarFactory.getCar("五菱");
Car car2 = CarFactory.getCar("特斯拉");
car.name();
car2.name();
}
}
运行截图
图形描述:
代码分析:
- 在这种工厂实现的情况下增加新的产品(类)实现起来会比较复杂。需要修改CarFactory类,因此违反了开闭原则。
- 虽然方法二比方法一添加新的类不需要修改内部逻辑、更加简单,但方法一与方法二本质上都是需要在原来代码的基础上增加新的代码才能添加新的产品(类),依旧不满足不修改原油代码的要求。
工厂方法模式
不修改已有类的前提下,可以增加产品类。
public interface CarFactory {
Car getCar();
}
public class TeslaFactory implements CarFactory {
public Car getCar() {
return new Tesla();
}
}
public class WuLingFactory implements CarFactory {
@Override
public Car getCar() {
return new WuLing();
}
}
public class Consumer {
public static void main(String[] args) {
Car car = new WuLingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
car.name();
car1.name();
}
}
运行截图:
新增一个产品
public class DaZhong implements Car{
@Override
public void name() {
System.out.println("大众!");
}
}
public class DaZhongFactory implements CarFactory {
@Override
public Car getCar() {
return new DaZhong();
}
}
public class Consumer {
public static void main(String[] args) {
Car car = new WuLingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
car.name();
car1.name();
Car car2 = new DaZhongFactory().getCar();
car2.name();
}
}
运行截图:
图形描述:
对比
- 结构复杂度:simple更简单
- 代码复杂度:simple更简单
- 编程复杂度:simple更简单
- 管理上的复杂度:simple更简单
根据设计原则:工厂方法模式
根据实际业务:简单工厂模式
3、抽象工厂模式
- 定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
- 适用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码(一次一起全部创建,很少更改)
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
- 优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
- 缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度
代码实现
手机和路由器产品接口
package factory.abstract_2;
public interface IPhoneProduct {
public void open();
public void callUp();
public void sendSMS();
public void shutDown();
}
package factory.abstract_2;
public interface IRouterProduct {
public void open();
public void setting();
public void openWifi();
public void shutDown();
}
华为和小米手机和路由器产品实现
package factory.abstract_2;
public class huaweiIPhone implements IPhoneProduct {
@Override
public void open() {
System.out.println("华为手机开机");
}
@Override
public void callUp() {
System.out.println("华为手机打电话");
}
@Override
public void sendSMS() {
System.out.println("华为手机发短信");
}
@Override
public void shutDown() {
System.out.println("华为手机关机");
}
}
package factory.abstract_2;
public class xiaomiIPhone implements IPhoneProduct {
@Override
public void open() {
System.out.println("小米手机开机");
}
@Override
public void callUp() {
System.out.println("小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("小米手机发短信");
}
@Override
public void shutDown() {
System.out.println("小米手机关机");
}
}
package factory.abstract_2;
public class huaweiIRouter implements IRouterProduct{
@Override
public void open() {
System.out.println("华为路由器打开");
}
@Override
public void setting() {
System.out.println("华为路由器设置");
}
@Override
public void openWifi() {
System.out.println("华为路由器打开无线网");
}
@Override
public void shutDown() {
System.out.println("华为路由器关机");
}
}
package factory.abstract_2;
public class xiaomiIRouter implements IRouterProduct{
@Override
public void open() {
System.out.println("小米路由器打开");
}
@Override
public void setting() {
System.out.println("小米路由器设置");
}
@Override
public void openWifi() {
System.out.println("小米路由器打开无线网");
}
@Override
public void shutDown() {
System.out.println("小米路由器关机");
}
}
抽象工厂接口
- 其本质就是工厂的工厂
package factory.abstract_2;
public interface IRouterProduct {
public void open();
public void setting();
public void openWifi();
public void shutDown();
}
抽象工厂的实现
package factory.abstract_2;
public class huaweiFactory implements ProductFactory {
@Override
public IPhoneProduct produceIPhone() {
return new huaweiIPhone();
}
@Override
public IRouterProduct produceIRouter() {
return new huaweiIRouter();
}
}
package factory.abstract_2;
public class xiaomiFactory implements ProductFactory{
@Override
public IPhoneProduct produceIPhone() {
return new xiaomiIPhone();
}
@Override
public IRouterProduct produceIRouter() {
return new xiaomiIRouter();
}
}
主程序
package factory.abstract_2;
public class Client {
public static void main(String[] args) {
System.out.println("===========小米系列产品===========");
xiaomiFactory xf=new xiaomiFactory();
IPhoneProduct xiaomiIPhoneProduct= xf.produceIPhone();
// xiaomiIPhone xiaomiiphone2= (xiaomiIPhone) xf.produceIPhone();
// xiaomiiphone2.open();
xiaomiIPhoneProduct.open();
xiaomiIPhoneProduct.callUp();
IRouterProduct xiaomiIRouterProduct = xf.produceIRouter();
xiaomiIRouterProduct.open();
xiaomiIRouterProduct.openWifi();
System.out.println();
System.out.println("===========华为系列产品===========");
huaweiFactory hf=new huaweiFactory();
IPhoneProduct huaweiIPhoneProduct = hf.produceIPhone();
huaweiIPhoneProduct.open();
huaweiIPhoneProduct.callUp();
IRouterProduct huaweiIRouterProduct = hf.produceIRouter();
huaweiIRouterProduct.open();
huaweiIRouterProduct.openWifi();
}
}
实现类图
精彩类图~
小结
- 简单工厂模式
- 虽然某种程度上不符合设计原则,但实际使用最多!
- 工厂方法模式
- 不修改已有类的前提下,通过增加新的工厂类实现扩展
- 抽象工厂模式
- 不可以增加产品,可以增加产族(也就是增加一个三星)!
- 应用场景:
- JDK中Calendar的getInstance方法
- JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法