文章目录
设计模式的分类
总的来说设计模式分为三大类
- 创建型模式(五种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
- 结构型模式(七种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
- 行为型模式(十一种):策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式。
这篇我们主要讲一下创建型模式。
由于我是java开发者,我会根据设计模式在java中的应用来说说。
工厂模式
简单工厂
定义
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
核心
为工厂类传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
俗话说
现在有一个车间(工厂),由于需求比较少,所以一个车间可以生产不同的但是相像产品(具体对象),这些对象都是从一个模子里刻出来的(抽象对象)
优点
实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程,只需知道具体产品类所对应的参数即可,通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
在于不符合"开闭原则",每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
具体实现
java
class ShapeFactory {
public static Shape getShape(String type) {
Shape shape = null;
if (type.equalsIgnoreCase("circle")) {
shape = new CircleShape();
} else if (type.equalsIgnoreCase("rect")) {
shape = new RectShape();
} else if (type.equalsIgnoreCase("triangle")) {
shape = new TriangleShape();
}
return shape;
}
}
工厂模式(Spring IOC控制反转)
工厂模式的作用就是创建对象
定义
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。
核心
也就是说每个对象都有一个与之对应的工厂。
组成
- AbstractFactory(抽象工厂):声明了一组用于创建对象的方法,注意是一组。
- ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建对象的方法,生成一组具体对象。
- AbstractProduct(抽象产品):它为每种对象声明接口,在其中声明了对象所具有的业务方法。
- ConcreteProduct(具体产品):它定义具体工厂生产的具体对象。
俗话说
有好多车间,由于需求比较多,所以需要不同的车间来生产不同的商品,这些车间(具体工厂)生产的产品(具体对象)都是单一的,提高效率,这些车间都是一个车间模子(抽象工厂)里刻出来的,产品也是一个模子(抽象对象)里刻出来的.
实现思路
工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
具体实现
就是将对象的具体创建交给工厂
- 产品类
java
//抽象产品
abstract class BMW {
public BMW(){}
}
//具体产品
public class BMW320 extends BMW {
public BMW320() {
System.out.println("制造-->BMW320");
}
}
//具体产品
public class BMW523 extends BMW{
public BMW523(){
System.out.println("制造-->BMW523");
}
}
- 工厂类
java
//抽象工厂
interface FactoryBMW {
BMW createBMW();
}
//具体工厂
public class FactoryBMW320 implements FactoryBMW{
@Override
public BMW320 createBMW() {
return new BMW320();
}
}
//具体工厂
public class FactoryBMW523 implements FactoryBMW {
@Override
public BMW523 createBMW() {
return new BMW523();
}
}
- 客户类(创建对象)
java
public class Customer {
public static void main(String[] args) {
FactoryBMW320 factoryBMW320 = new FactoryBMW320();
BMW320 bmw320 = factoryBMW320.createBMW();
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
BMW523 bmw523 = factoryBMW523.createBMW();
}
}
使用场景
- 客户端不需要知道它所创建的对象的类
- 客户端可以通过子类来指定创建对应的对象。
抽象工厂模式
前提概念
- 产品等级结构
产品等级结构指的是产品的继承结构,例如一个空调抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个空调抽象类和他的子类就构成了一个产品等级结构。
- 产品族
产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调、海尔冰箱,那么海尔空调则位于空调产品族中。
- 实体图
定义
**抽象工厂模式主要用于创建相关对象的家族。**当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象
并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
缺点
在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。
具体实现
- 产品类
java
//发动机以及型号
public interface Engine {}
public class EngineA implements Engine{
public EngineA(){
System.out.println("制造-->EngineA");
}
}
public class EngineB implements Engine{
public EngineB(){
System.out.println("制造-->EngineB");
}
}
//空调以及型号
public interface Aircondition {}
public class AirconditionA implements Aircondition{
public AirconditionA(){
System.out.println("制造-->AirconditionA");
}
}
public class AirconditionB implements Aircondition{
public AirconditionB(){
System.out.println("制造-->AirconditionB");
}
}
- 工厂类
java
//创建工厂的接口
public interface AbstractFactory {
//制造发动机
public Engine createEngine();
//制造空调
public Aircondition createAircondition();
}
//为宝马320系列生产配件
public class FactoryBMW320 implements AbstractFactory{
//发动机
@Override
public Engine createEngine() {
//在这里新建一个对象
return new EngineA();
}
//空调
@Override
public Aircondition createAircondition() {
//在这里新建一个对象
return new AirconditionA();
}
}
//为宝马523系列胜场配件
public class FactoryBMW523 implements AbstractFactory {
//发动机
@Override
public Engine createEngine() {
//在这里新建一个对象
return new EngineB();
}
//空调
@Override
public Aircondition createAircondition() {
//在这里新建一个对象
return new AirconditionB();
}
}
- 客户类
java
public class Customer {
public static void main(String[] args){
//生产宝马320系列配件
//创建具体工厂对象
FactoryBMW320 factoryBMW320 = new FactoryBMW320(); //通过具体工厂对象创建同一族产品
factoryBMW320.createEngine();
factoryBMW320.createAircondition();
//生产宝马523系列配件
FactoryBMW523 factoryBMW523 = new FactoryBMW523();
factoryBMW523.createEngine();
factoryBMW523.createAircondition();
}
}
- 产品->工厂->客户使用
单例模式
定义
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
俗话说
类似与你的心脏,在你的身体里有且只有一个,无论怎么变化,它都是同一个对象。
特点
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
作用
单例模式就是为了避免不一致状态,避免政出多头。
分类
- 懒汉式(懒得自己动,靠别人调用)
在第一次调用的时候实例化自己
java
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法(当第一次调用getInstance() 方法的时候自动创建对象)
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
- 饿汉式(饿得不行了,类刚加载的时候就自己实例化)
在类初始化时,已经自行实例化
java
public class Singleton1 {
private Singleton1() {}
//类初始化的时候就实例化对象,而不是调用方法才实例化
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
- 线程安全的单例模式(进阶版:加了个线程同步)
在懒汉式单例模式基础上加上线程同步
原理
代码对静态方法 getInstance()进行同步,以确保多线程环境下只创建一个实例。
如果getInstance()方法未被同步,并且线程A和线程B同时调用此方法,则执行if (instance == null)语句时都为真,那么线程A和线程B都会创建一个对象,在内存中就会出现两个对象,这样就违反了单例模式。而使用synchronized关键字进行同步后,则不会出现此种情况。
java
public class Singleton {
private static Singleton instance = null;
// 私有构造方法,保证外界无法直接实例化。
private Singleton() {}
// 通过公有的静态方法获取对象实例
synchronized public static Singleton getInstace() {
if (instance == null) {
instance = new Singleton(); }
return instance; }
}
建造者模式
建造者模式是一种创建型设计模式,也叫生成器模式。
定义
封装一个复杂对象构造过程,并允许按步骤构造。
就是将复杂对象的创建过程拆分成多个简单对象的创建过程,并将这些简单对象组合起来构建出复杂对象。
建造者模式是一种对象创建型模式,它将构建复杂对象的过程和表示进行分离,使得同样的构建过程可以创建不同的表示。
优点
- 灵活:可以分步骤地构建复杂对象,使得构建过程更加灵活。
- 解耦:可以隔离复杂对象的创建和使用,客户端不必关心对象的创建细节。
- 易扩展:增加新的具体建造者很方便,可以扩展构建器功能,符合开闭原则。
缺点
-
增加工作量:需要额外的代码来创建和管理具体建造者类,增加了程序员的工作量。
-
效率低:相比于其他创建型模式,在运行时效率较低,特别是对象太复杂时。
角色组成
- 产品类(Product):表示被创建的复杂对象。它通常包含多个部分或者组成,并由具体的建造者逐步构建而成。
- 抽象建造者类(Builder):定义了建造复杂对象所需要的各个部分的创建方法。它通常包括多个构建方法和一个返回产品的方法。
- 具体建造者类(ConcreteBuilder):实现Builder接口,并提供各个部分或者组成的构建方法。
- 指挥者类(Director):负责控制建造者的构建顺序,指挥建造者如何构建复杂对象。
应用场景
- 生活场景
- 盒饭套餐:顾客可以选择不同的菜,服务员按照顾客的要求,将这些菜组合起来,最终构建出一个完整的套餐。
- 盖房子:需要分多个阶段进行,比如准备材料、打地基、盖围墙...。建造者模式可以将房屋的建造分解成多个步骤,每个步骤对应一个具体的建造者,最终由包工头(指导者)来调用不同的建造者,完成整个房子的建造。
- java场景
- StringBuilder:能够动态地构建字符串。
- Stream API:将集合类转为stream流,通过一系列的中间操作和终止操作来生成最终结果。
- Lombok的@Builder注解:一个注解就可以生成建造者模式的代码。
代码实现
肯德徳都吃过吧,里面有很多的套餐。假设套餐主要由汉堡、薯条和饮料三种组成,每个组件都有不同种类和大小,并且每个套餐的组合方式也不同。下面以肯德徳套餐为例,解释建造者模式。
- 产品类:Meal
java
/**
* 1.产品类(Product)
*/
@Data
public class Meal {
//汉堡包
private String burger;
//薯条
private String fries;
//饮料
private String drink;
}
- 抽象建造者类:MealBuilder
java
/**
* 2.抽象建造者(Builder)
*/
public abstract class MealBuilder {
protected Meal meal=new Meal();
//构建汉堡
public abstract void buildBurger();
//构建薯条
public abstract void buildFries();
//构建饮料
public abstract void buildDrink();
public Meal getMeal(){
return meal;
}
}c
- 具体建造者类:BeefBurgerMealBuilder、ChickenMealBuilder、ShrimpMealBuilder
java
/**
* 3.具体建造者(ConcreteBuilder):鸡肉汉堡套餐
*/
public class ChickenMealBuilder extends MealBuilder{
@Override
public void buildBurger() {
meal.setBurger("鸡肉汉堡");
}
@Override
public void buildFries() {
meal.setFries("中份薯条");
}
@Override
public void buildDrink() {
meal.setDrink("大杯果汁");
}
}
/**
* 3.具体建造者(ConcreteBuilder):牛肉汉堡套餐
*/
public class BeefBurgerMealBuilder extends MealBuilder {
@Override
public void buildBurger() {
meal.setBurger("牛肉汉堡");
}
@Override
public void buildFries() {
meal.setFries("大份薯条");
}
@Override
public void buildDrink() {
meal.setDrink("中杯可乐");
}
}
/**
* 3.具体建造者(ConcreteBuilder):虾肉汉堡套餐
*/
public class ShrimpMealBuilder extends MealBuilder {
@Override
public void buildBurger() {
meal.setBurger("虾肉汉堡");
}
@Override
public void buildFries() {
meal.setFries("小份薯条");
}
@Override
public void buildDrink() {
meal.setDrink("大杯芬达");
}
}
- 指挥者类:MealDirector
java
/**
* 4.指导者(Director)
*/
public class MealDirector {
private MealBuilder mealBuilder;
public void setMealBuilder(MealBuilder mealBuilder){
this.mealBuilder=mealBuilder;
}
public Meal getMeal(){
return mealBuilder.getMeal();
}
//制作套餐
public void constructMeal(){
mealBuilder.buildBurger();
mealBuilder.buildFries();
mealBuilder.buildDrink();
}
}
- 测试
java
/**
* 建造者模式测试类
*/
@SpringBootTest
public class TestBuilder {
@Test
void testBuilder(){
//创建指导者
MealDirector director=new MealDirector();
//执导建造牛肉套餐
director.setMealBuilder(new BeefBurgerMealBuilder());
director.constructMeal();
Meal meal = director.getMeal();
System.out.println("牛肉套餐:"+meal.toString());
//鸡肉套餐
director.setMealBuilder(new ChickenMealBuilder());
director.constructMeal();
Meal meal2 = director.getMeal();
System.out.println("鸡肉套餐:"+meal2.toString());
//虾肉套餐
director.setMealBuilder(new ShrimpMealBuilder());
director.constructMeal();
Meal meal3 = director.getMeal();
System.out.println("虾肉套餐:"+meal3.toString());
}
}
总结
使用场景:
- 当需要创建一些特定的对象,但是它们拥有共同的组成部分时,比如:一个房子可以由个个部件:框架、墙、窗户等,这些部件可以组合起来构造完整的房子。
- 当对象的构建过程比较复杂且需要多个步骤时,例如,创建一份电子商务订单需要多个步骤,如选择商品、填写地址和支付等,这些步骤可以被分别封装成为订单构建器中的不同方法。
- 当需要创建一些特定类型的对象,例如复杂的数据结构或配置对象时,这在编写配置文件解析器以及通用数据结构如二叉树等时很有用。
- 建造者模式也可以被用于通过更高级的方式来构建复杂对象,例如:序列化和反序列化。
与抽象工厂模式的区别:
抽象工厂模式强调的是产品族的创建,即相关的产品一起被创建出来,而建造者模式强调的是一个复杂对象的创建,即它的各个部分逐步被创建出来。