几种简单设计模式介绍
  wMDCgnRK3Ovr 2023年11月21日 40 0

一、单例模式

1.模式动机

  • 对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
  • 如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象
  • 一个更好的解决方法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

2.模式定义

  • 单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
  • 单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

3.模式结构

几种简单设计模式介绍_工厂方法模式

  • 单例模式包含如下角色
  • Singleton:单例

4.模式分析

//懒汉式
public class Singleton {
    //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
    //声明为volatile类型的变量,防止高并发环境下指令重排序造成问题
    private volatile static Singleton instance = null;
    //私有构造方法,防止被实例化
    private Singleton() {
        
    }
    //懒汉式。静态工厂方法。创建实例
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
//饿汉式
public class Singleton {
    //持有私有静态实例,防止被引用
    private static Singleton instance = new Singleton();
    //私有构造方法,防止被实例化
    private Singleton() {
        
    }
    //饿汉式,静态工厂方法,创建实例
    public static Singleton getInstance() {
        return instance;
    }
}
  • 单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类---Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化子集,然后存储在静态成员变量中,以确保只有一个实例被创建
  • 在单例模式的实现过程中,需要注意如下三点:
  • 单例类的构造函数为私有;
  • 提供一个自身的静态私有成员变量;
  • 提供一个公有的静态工厂方法。

5.使用场景

  • 要求生成唯一序列号的环境
  • 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式可以保持计数器的值,并确保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static)。

二、工厂模式

工厂模式不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,这个函数被称为工厂。工厂模式又可以分为简单工厂模式、工厂方法模式和抽象工厂模式。

2.1 简单工厂模式

2.1.1 模式动机
  • 只需要知道水果的名字则可得到相应的水果

几种简单设计模式介绍_工厂方法模式_02

  • 考虑一个简单的软件应用场景,一个软件系统可以提供多个外观不同的按钮(如圆形按钮、矩形按钮、菱形按钮等),这些按钮都源自同一个基类,不过在继承基类后不同的子类修改了部分属性从而使得它们可以呈现不同的外观,如果我们希望在使用这些按钮时,不需要知道这些具体按钮类的名字,只需要知道表示该按钮类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的按钮对象,此时,就可以使用简单工厂模式。
2.1.2 模式定义
  • 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
2.1.3 模式结构

几种简单设计模式介绍_工厂方法模式_03

  • 简单工厂模式包含如下角色:
  • Factory:工厂角色
  • Product:抽象产品角色
  • ConcreteProduct:具体产品角色
2.1.4 模式分析
//抽象支付类
public abstract class AbstractPay {
    public abstract void pay();
}
//具体支付类
public class CashPay extends AbstractPay {
    public void pay() {
        //现金支付处理代码
    }
}
//具体支付类
public class CreditcardPay() {
    public void pay() {
        
    }
}
//支付工厂
public class PayMethodFactory {
    public static AbstractPay getPayMethod(String type) {
        if (type.equalsIgnoreCase("cash")) {
            return new CashPay(); 
        } else if (type.equalsIgnoreCase("credicard")) {
            return new CreditcardPay();
        }
    }
}
  • 将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
  • 在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方便,可通过类名直接调用,而且只需要传入一个简单的参数即可,在实际开发中,还可以再调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无需修改任何Java源代码,
  • 简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。
  • 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节。
2.1.5 使用场景

JDBC链接数据库,硬件访问,降低对象的产生和销毁。

2.2 工厂方法模式

2.2.1 模式动机
  • 考虑这样一个系统,按钮工厂类可以返回一个具体的按钮实例,如圆形按钮,矩形按钮,菱形按钮等。在这个系统中,如果需要增加一种新类型的按钮,如椭圆形按钮,那么除了增加一个新的具体产品类之外,还需要修改工厂类的代码,这就使得整个设计在一定程度上违反了“开闭原则”。

几种简单设计模式介绍_工厂类_04

  • 现在对该系统进行修改,不再设计一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完成,我们先定义一个抽象的按钮工厂类,再定义具体的工厂类来生成圆形按钮,矩形按钮,菱形按钮等,它们实现在抽象按钮工厂类中定义的方法。这种抽象化的结果使这种结构可以再不修改具体工厂类的情况下引进新的产品。如果出现新的按钮类型,只需要为这种新类型的按钮创建一个具体的工厂类就可以获得该新按钮的实例,这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性,更加符合“开闭原则”。

几种简单设计模式介绍_工厂类_05

2.2.2 模式定义
  • 工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
2.2.3 模式结构

几种简单设计模式介绍_简单工厂模式_06

  • 工厂方法模式包含如下角色:
  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂
2.2.4 模式分析
  • 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的有点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
  • 当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好地符合了“开闭原则”。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。工厂方法模式退化后可以演变为简单工厂模式。
//抽象工厂类
public abstract class PayMethodFactory {
    public abstract AbstractPay getPayMethod();
}
//具体工厂类
public class CashPayFactory extends PayMethodFactory {
    public AbstractPay getPayMethod() {
        return new CashPay();
    }
}
//客户类代码片段
PayMethodFactory factory;
AbstractPay payMethod;
factory = new CashPayFactory();
payMethod = factory.getPayMethod();
payMethod.pay();

为了提高系统的可扩展性和灵活性,在定义工厂和产品时都必须使用抽象层,如果需要更换产品类,只需要更换对应的工厂即可,其他代码不需要进行任何修改。

2.2.5 使用场景

日志记录器

几种简单设计模式介绍_工厂方法模式_07

2.3 抽象工厂模式

2.3.1 模式动机
  • 在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
  • 为了更清晰地理解工厂方法模式,需要先引入两个概念:
  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是子类。
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

几种简单设计模式介绍_简单工厂模式_08

  • 当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
  • 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
  • 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

几种简单设计模式介绍_工厂类_09

2.3.2 模式定义
  • 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式又称为kit模式,属于对象创建型模式。
2.3.3 模式结构

几种简单设计模式介绍_简单工厂模式_10

  • 抽象工厂模式包含如下角色:
  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • Product:具体产品
2.3.4 模式分析

几种简单设计模式介绍_简单工厂模式_11

几种简单设计模式介绍_工厂方法模式_12

几种简单设计模式介绍_工厂类_13


//抽象工厂类
public abstract class AbstractFactory {
	public abstract AbstractProductA createProductA();
	public abstract AbstractProductB createProductB();
}
//具体工厂类
public class ConcreteFactory1 extends AbstractFactory {
	public AbstractProductA createProductA() {
		return new ConcreteProductA1();
	}
	public AbstractProductB createProductB() {
		return new ConcreteProductB1();
	}
}
2.3.5 使用场景
  • 数据库操作工厂:某系统为了改进数据库操作的性能,自定义数据库连接对象Connection和语句对象Statement,可针对不同类型的数据库提供不同的连接对象和语句对象,如提供Oracle或SQL Server专用连接类和语句类,而且用户可以通过配置文件等方式根据实际需要动态更换系统数据库。使用抽象工厂模式设计该系统。

几种简单设计模式介绍_工厂方法模式_14

三、建造者模式

3.1 模式动机

  • 类的属性很多,使用构造函数创建类时构造函数的参数列表过长,影响代码的可读性和易用性。
  • 可以使用set()方法来设置属性,但是必填属性又需要通过构造方法设置,如果必填属性过多,构造函数的参数又会很多。
  • 另外,如果需要创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,就不能在类中暴露set()方法。构造函数配合set()方法来设置属性值的方式就不适用了。

3.2 模式定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

3.3 模式分析

class Student {
    private String name = null;
    private int number = -1;
    private String sex = null;
    public Student (Builder builder) {
        this.name = builder.name;
        this.number = builder.number;
        this.sex = builder.sex;
    }
    public String getName() {
        return this.name;
    }

    public int getNumber() {
        return this.number;
    }

    public String getSex() {
        return this.sex;
    }

    static class Builder {
        private String name = null;
        private int number = -1;
        private String sex = null;
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        public Builder setNumber(int number) {
            this.number = number;
            return this;
        }
        public Builder setSex(String sex) {
            this.sex = sex;
            return this;
        }
        public Student build() {
            return new Student(this);
        }
    }

}

public class Build {
    public static void main(String[] args) {
        Student A = new Student.Builder().setName("zhangsan").setNumber(1).build();
        System.out.println(A.getName() + ", " + A.getNumber() + ", " + A.getSex());
    }
}

在类的内部定义一个静态内部类,内部类的属性和外部类相同,并且内部类定义了返回值是内部类类型的set方法,这样就可以链式调用。外部类的构造方法中传入一个内部类类型的参数,然后利用内部类的属性设置外部类的属性,内部类中有一个方法build(),该方法调用外部类的构造方法,将自身传进去。

3.4 使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候可以使用建造者模式。

四、责任链模式

4.1 模式定义

创建多个对象,使这些对象形成一条链,并沿着这条链传递请求,直到链上的某一个对象决定处理此请求。

  • 接受请求的对象连接成一条链,对象之间存在层级关系;
  • 这些对象可以处理请求,也可以传递请求,直到有对象处理该请求。

4.2 模式结构

几种简单设计模式介绍_工厂方法模式_15

责任链模式涉及到的角色如下:

  • 抽象处理者角色:定义了处理请求的接口或者抽象类,提供了处理请求的方法和设置下一个处理者的方法;
  • 具体处理者角色:实现或者继承抽象者角色,具体逻辑根据实际的架构来定。

4.3 模式分析

//抽象处理者
public abstract class Handler {
	private Handler nextHandler;
	private int level;

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

	//处理请求传递,注意final,子类不可重写
	public final void handleMessage(Demand demand) {
		if (level == demand.demandLevel) {
			this.report(demand);
		} else {
			if (this.nextHandler != null) {
				System.out.println("传递给下一级");
			} else {
				System.out.println("没有下一级了");
			}
		}
	}

	public void setNextHandler(Handler handler) {
		this.nextHandler = handler;
	}

	//抽象方法,子类实现
	public abstract void report(Demand demand);

}

在抽象处理者角色定义了处理请求的抽象方法,以及下一级传递的对象方法,重点在handleMessage处理请求传递的方法。

下面是具体处理者角色,继承抽象处理者角色,在例子中有两个具体处理者,分别是技术经理和boss。

/技术经理
public class TechnicalManager extends Handler {
	public TechnicalManager() {
		super(1);
	}

	@Override
	public void report(Demand demand) {
		System.out.println("需求: " + demand.detail());
		System.out.println("我是产品经理");
	}
}

//boss
public class boss extends Handler {
	public Boss() {
		super(2);
	}

	@Override
	public void report(Demand demand) {
		System.out.println("需求: " + demand.detail());
		System.out.println("我是boss");
	}
}

可以看到具体处理者的代码很简洁,重写了report方法,实现各自的业务逻辑,这都归功于父类中handleMessage这个方法。

两个角色都定义好了,来看看客户端如何实现。

public class Client {
	public static void main(String[] args) {
		Demand demandA = new DemandA();  //请求等级低
		Demand demandB = new DemandB();  //请求等级高

		Boss boss = new Boss();
		TechnicalManager technicalManager = new TechnicalManager();
		technicalManager.setNextHandler(boss);  //设置下一级

		technicalManager.handleMessage(demandA);
		System.out.println("=====================");
		technicalManager.handleMessage(demandB);
	}
}

客户端中的重点是设置下一级的处理者,这样多个处理者对象就会形成一条链。级别低的请求技术经理自己处理,级别高的传递给了下一级的Boss,这样就形成一条链,而这也是责任链的核心所在。注意,在请求传递的过程中,请求是不会发生变化的。

责任链的优点

  • 降低耦合度:客户端不需要知道请求由哪个处理者处理,而处理者也不需要知道处理者之间的传递关系,有系统灵活地组织和分配。
  • 良好的扩展性:增加处理者的实现很简单,只需要重写处理请求业务逻辑的地方。

责任链的缺点

  • 请求会从链头发出,直到有处理者响应,在责任链比较长的时候会影响系统性能;
  • 请求递归,调试排错比较麻烦;

责任链与观察者模式的区别

责任链模式和观察者模式存在一个共同点,就是传递。责任链模式是一级一级的传递,形成一条链,链节点(处理者)之间是存在传递关系的。而观察者模式的被观察者向观察者们的传递,并不是具体观察者之间的传递,观察者之间是不存在关联的。责任链模式有层级关系,而观察者模式是扩散式的没没有层级关系。

4.4 应用场景

  • 会员等级系统,会员等级之间构成一条链,用户发起一个请求,系统只要把请求分发到责任链模式的入口,直到传递到与用户会员等级匹配的等级,这样各个会员等级的业务逻辑就会变得很清晰。
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月21日 0

暂无评论

wMDCgnRK3Ovr