设计模式-创新型模式
简介
设计模式是软件开发中的一种通用解决方案,它们是经过实践证明的、被广泛接受的最佳实践。设计模式提供了一种可重用的设计思想,可以帮助开发人员解决常见的设计问题,提高代码质量和可维护性。
设计模式通常包含以下元素:
模式名称:用于描述模式的名称。
问题描述:描述模式所解决的问题,包括场景和限制条件。
解决方案:描述模式的解决方案,包括结构、参与者、协作方式和责任。
优点和缺点:描述模式的优点和缺点,包括可维护性、可扩展性、可重用性等方面。
适用性:描述模式适用的场景和限制条件。
实现方式:描述模式的实现方式,包括代码示例和实现细节。
创新型模式
简单工厂模式
简介
- 简单工厂模式又叫静态方法模式(因为工厂类定义了一个静态方法)
现实生活中,工厂是负责生产产品的;
- 同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为“工厂类”。
解决问题
- 将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产的细节。
- 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
- 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。
模式原理
组成 |
关系 |
作用 |
抽象产品 |
具体产品的父类 |
描述产品的公共接口 |
具体产品 |
抽象产品的子类;工厂类创建的目标类 |
描述生产的具体产品 |
工厂 |
被外界调用 |
根据传入不同参数从而创建不同具体产品类的实例 |
UML类图
举例实现
abstract class Product{ public abstract void Show(); }
|
class ProductA extends Product{
@Override public void Show() { System.out.println("生产出了产品A"); } }
class ProductB extends Product{
@Override public void Show() { System.out.println("生产出了产品C"); } }
class ProductC extends Product{
@Override public void Show() { System.out.println("生产出了产品C"); } }
|
class Factory { public static Product Manufacture(String ProductName){
switch (ProductName){ case "A": return new ProductA();
case "B": return new ProductB();
case "C": return new ProductC();
default: return null;
} } }
|
public class SimpleFactoryPattern { public static void main(String[] args){ Factory mFactory = new Factory();
try {
mFactory.Manufacture("A").Show(); }catch (NullPointerException e){ System.out.println("没有这一类产品"); }
try { mFactory.Manufacture("B").Show(); }catch (NullPointerException e){ System.out.println("没有这一类产品"); }
try { mFactory.Manufacture("C").Show(); }catch (NullPointerException e){ System.out.println("没有这一类产品"); }
try { mFactory.Manufacture("D").Show(); }catch (NullPointerException e){ System.out.println("没有这一类产品"); } } }
|
生产出了产品A 生产出了产品C 生产出了产品C 没有这一类产品
|
问题
- 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
- 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
- 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
工厂方法模式
简介
- 工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
- 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。
解决问题
- 具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点
- 新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
- 符合单一职责原则,每个具体工厂类只负责创建对应的产品
- 不使用静态工厂方法,可以形成基于继承的等级结构。简单工厂模式的工厂类使用静态工厂方法
模式组成
组成 |
关系 |
作用 |
抽象产品 |
具体产品的父类 |
描述具体产品的公共接口 |
具体产品 |
抽象产品的子类;工厂类创建的目标类 |
描述生产的具体产品 |
抽象工厂 |
具体工厂的父类 |
描述具体工厂的公共接口 |
具体工厂 |
抽象工厂的子类;被外界调用 |
描述具体工厂;实现FactoryMethod工厂方法创建产品的实例 |
UML类图
实例举例
abstract class Factory{ public abstract Product Manufacture(); }
|
abstract class Product{ public abstract void Show(); }
|
class ProductA extends Product{ @Override public void Show() { System.out.println("生产出了产品A"); } }
class ProductB extends Product{
@Override public void Show() { System.out.println("生产出了产品B"); } }
|
class FactoryA extends Factory{ @Override public Product Manufacture() { return new ProductA(); } }
class FactoryB extends Factory{ @Override public Product Manufacture() { return new ProductB(); } }
|
public class FactoryPattern { public static void main(String[] args){ FactoryA mFactoryA = new FactoryA(); mFactoryA.Manufacture().Show();
FactoryB mFactoryB = new FactoryB(); mFactoryB.Manufacture().Show(); } }
|
问题
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度
- 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
应用
- 当一个类不知道它所需要的对象的类时,在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
- 当一个类希望通过其子类来指定创建对象时,在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式
简介
- 抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
- 抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类
- 允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。
解决问题
- 降低耦合,抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展;
- 更符合开-闭原则,新增一种产品类时,只需要增加相应的具体产品类和相应的工厂子类即可
模式组成
组成 |
关系 |
作用 |
抽象产品族 |
抽象产品的父类 |
描述抽象产品的公共接口 |
抽象产品 |
具体产品的父类 |
描述具体产品的公共接口 |
具体产品 |
抽象产品的子类;工厂类创建的目标类 |
描述生产的具体产品 |
抽象工厂 |
具体工厂的父类 |
描述具体工厂的公共接口 |
具体工厂 |
抽象工厂的子类;被外界调用 |
描述具体工厂;实现工厂方法创建产品的实例 |
UML类图
举例实例
abstract class Factory{ public abstract Product ManufactureContainer(); public abstract Product ManufactureMould(); }
|
abstract class AbstractProduct{ public abstract void Show(); }
|
abstract class ContainerProduct extends AbstractProduct{ @Override public abstract void Show(); }
abstract class MouldProduct extends AbstractProduct{ @Override public abstract void Show(); }
|
class ContainerProductA extends ContainerProduct{ @Override public void Show() { System.out.println("生产出了容器产品A"); } }
class ContainerProductB extends ContainerProduct{ @Override public void Show() { System.out.println("生产出了容器产品B"); } }
class MouldProductA extends MouldProduct{
@Override public void Show() { System.out.println("生产出了模具产品A"); } }
class MouldProductB extends MouldProduct{
@Override public void Show() { System.out.println("生产出了模具产品B"); } }
|
class FactoryA extends Factory{
@Override public Product ManufactureContainer() { return new ContainerProductA(); }
@Override public Product ManufactureMould() { return new MouldProductA(); } }
class FactoryB extends Factory{
@Override public Product ManufactureContainer() { return new ContainerProductB(); }
@Override public Product ManufactureMould() { return new MouldProductB(); } }
|
public class AbstractFactoryPattern { public static void main(String[] args){ FactoryA mFactoryA = new FactoryA(); FactoryB mFactoryB = new FactoryB(); mFactoryA.ManufactureContainer().Show(); mFactoryA.ManufactureMould().Show();
mFactoryB.ManufactureContainer().Show(); mFactoryB.ManufactureMould().Show();
} }
|
生产出了容器产品A 生产出了容器产品B 生产出了模具产品A 生产出了模具产品B
|
问题
单例模式
简介
- 实现1个类只有1个实例化对象 & 提供一个全局访问点
- 从上面可看出:工人类操作的明显不是同一个仓库实例,而全部工人希望操作的是同一个仓库实例,即只有1个实例
工作原理
- 在Java中,我们通过使用对象(类实例化后)来操作这些类,类实例化是通过它的构造方法进行的,要是想实现一个类只有一个实例化对象。
举例实现
public class Singleton {
private static Singleton ourInstance = new Singleton();
private Singleton() { }
public static Singleton newInstance() { return ourInstance; } }
|
情景代入
背景:小成有一个塑料生产厂,但里面只有一个仓库。
目的:想用代码来实现仓库的管理
现有做法: 建立仓库类和工人类 其中,仓库类里的quantity=商品数量;工人类里有搬运方法MoveIn(int i)和MoveOut(int i)。
一般实现
class StoreHouse { private int quantity = 100;
public void setQuantity(int quantity) { this.quantity = quantity; }
public int getQuantity() { return quantity; } }
class Carrier{ public StoreHouse mStoreHouse; public Carrier(StoreHouse storeHouse){ mStoreHouse = storeHouse; } public void MoveIn(int i){ mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i); } public void MoveOut(int i){ mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i); } }
public class SinglePattern { public static void main(String[] args){ StoreHouse mStoreHouse1 = new StoreHouse(); StoreHouse mStoreHouse2 = new StoreHouse(); Carrier Carrier1 = new Carrier(mStoreHouse1); Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("两个是不是同一个?");
if(mStoreHouse1.equals(mStoreHouse2)){ System.out.println("是同一个"); }else { System.out.println("不是同一个"); } Carrier1.MoveIn(30); System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity()); Carrier2.MoveOut(50); System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity()); } }
|
单例实现
package scut.designmodel.SingletonPattern;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
class StoreHouse {
private int quantity = 100; private static StoreHouse ourInstance = new StoreHouse();; public static StoreHouse getInstance() { return ourInstance; }
private StoreHouse() { }
public void setQuantity(int quantity) { this.quantity = quantity; }
public int getQuantity() { return quantity; } }
class Carrier{ public StoreHouse mStoreHouse; public Carrier(StoreHouse storeHouse){ mStoreHouse = storeHouse; } public void MoveIn(int i){ mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i); } public void MoveOut(int i){ mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i); } }
public class SinglePattern { public static void main(String[] args){ StoreHouse mStoreHouse1 = StoreHouse.getInstance(); StoreHouse mStoreHouse2 = StoreHouse.getInstance(); Carrier Carrier1 = new Carrier(mStoreHouse1); Carrier Carrier2 = new Carrier(mStoreHouse2);
System.out.println("两个是不是同一个?");
if(mStoreHouse1.equals(mStoreHouse2)){ System.out.println("是同一个"); }else { System.out.println("不是同一个"); } Carrier1.MoveIn(30); System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity()); Carrier2.MoveOut(50); System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity()); } }
|
多种单例模式实现方式
饿汉式
- 依赖JVM类加载机制,保证单例只会被创建一次,即线程安全
- JVM在类的初始化阶段,会执行类的初始化
- 在执行类的初始化的时候,JVM会去获取一个锁,这个所可以同步多个线程对同一个类的初始化
class Singleton {
private static Singleton ourInstance = new Singleton(); private Singleton() { } public static Singleton newInstance() { return ourInstance; } }
|
枚举类型
- 根据枚举类型的下述特点,满足单例模式所需的创建单例,线程安全,实现简介的需求
public enum Singleton{
INSTANCE;
}
Singleton singleton = Singleton.INSTANCE;
|
懒汉式
class Singleton { private static Singleton ourInstance = null;
private Singleton() { } public static Singleton newInstance() { if( ourInstance == null){ ourInstance = new Singleton(); } return ourInstance; } }
|
问题
- 对于懒汉会存在线程不安全的时候,特别是在多线程时候会出现以下问题
同步锁
- 使用同步锁synchronized,锁住创建单例的方法,防止多个线程同时调用,从而避免造成单例被多次创建
- getInstance()方法块只能运行在一个线程中
- 倘若此段代码已在一个线程中运行,则另外一个线程试图运行该块代码,则会被一直阻塞二一直等待。
实现
class Singleton { private static Singleton ourInstance = null; private Singleton() { }
public static synchronized Singleton getInstance(){ if ( ourInstance == null ) ourInstance = new Singleton(); return ourInstance; } }
class Singleton{
private static Singleton instance = null;
private Singleton(){ }
public static Singleton getInstance(){ synchronized(Singleton.class) { if (instance == null) instance = new Singleton(); } return instance; } }
|
双重校验锁
- 在同步锁的基础上,添加多一层if判断,若单例已经创建,则不需要执行加锁操作就可以获取实例,从而提高性能
实现
class Singleton { private static Singleton ourInstance = null;
private Singleton() { } public static Singleton newInstance() { if( ourInstance == null){ synchronized (Singleton.class){ if( ourInstance == null){ ourInstance = new Singleton(); } } } return ourInstance; } }
|
静态内部类
- 更具静态内部类的特性,同步解决按需加载,线程安全的问题,同时实现简洁
- 在静态内部类中创建单例,在装载该内部类的时候才会去创建单例
- 线程安全:类是由JVM加载,而JVM只会加载一遍,保证只有一个单例
实现
class Singleton { private static class Singleton2 { private static Singleton ourInstance = new Singleton(); }
private Singleton() { } public static Singleton newInstance() { return Singleton2.ourInstance; }
}
|
总结
建造者模式
简介
- 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
- 建造者模式负责按照是顺序创建复杂对象(把内部的建造过程和细节隐藏藏匿起来)
解决问题
- 方便用户创建复杂的对象
- 代码复用性以及封装性(将对象构建过程和细节进行封装以及复用)
- 方便解耦,方便控制对象的创建,方便于拓展。
UML类图
模式讲解
- 指挥者直接和客户进行需求沟通
- 沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求
- 将各个部件的建造请求委派到具体的建造者
- 各个具体建造者负责进行产品部件的构建
- 最终构建成具体产品。
情景代入
- 背景:小成希望去电脑城买一台组装的台式主机
- 过程:
- 电脑城老板(Diretor)和小成(Client)进行需求沟通(买来打游戏?学习?看片?)
- 了解需求后,电脑城老板将小成需要的主机划分为各个部件(Builder)的建造请求(CPU、主板blabla)
- 指挥装机人员(ConcreteBuilder)去构建组件;
- 将组件组装起来成小成需要的电脑(Product);
举例实例
- 定义组装的过程:组装电脑的过程
public abstract class Builder{
public abstract void BuildCPU();
public abstract void BuildMainboard();
public abstract void BuildHD();
public abstract Computer GetCompputer(); }
|
- 电脑城老板委派任务给装机人员
public class Director{
public void Construct(Builder builder){ builder.BuildCPU(); builder.BuildMainboard(); builder.BuuildHD(); } }
|
- 创建具体的建造者:装机人员
public class ConcreteBuilder extends Builder { Computer computer = new Computer();
@Override public void BuildCPU(){ computer.Add("组装CPU"); } @Override public void BuilMainboard(){ computer.Add("组装主板"); } @Override public void BuildHD(){ computer.Add("组装硬盘"); } @Override public Computer GetComputer(){ return computer; } }
|
- 定义具体产品类:电脑
public class Computer{
private List<String> parts = new ArrayList<String>();
public void Add(String part){ part.add (part); } public void Show(){ for(int i =0;<part.size();i++){ System.out.println("组件"+part.get(i)+"装好了"); } System.out.println("电脑组装完成,请验收"); } }
|
- 客户端调用-小成到电脑找了老板买电脑
public class Builder Pattern{ public static void main(String[] args){
Director director = new Director(); Builder builder = new ConcreteBuilder();
director.Construct(builder);
Computer computer = builder.GetComputer();
computer.Show();
} }
|
问题
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
原型模式
简介
- 它允许通过复制现有的对象来创建新的对象,而不是通过创建新的实例并初始化它们来创建对象。这种模式通常用于创建具有相同属性的多个对象,以避免重复的初始化过程。在原型模式中,原型对象是创建新对象的模板,新对象是通过复制原型对象来创建的。原型模式的实现通常需要实现 Cloneable 接口或者使用序列化和反序列化来实现对象的复制。
UML类图
解决问题
举例实例
public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
|
public class Rectangle extends Shape { public Rectangle(){ type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
|
public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } }
|
public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }
|
import java.util.Hashtable; public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } }
|
public class PrototypePatternDemo { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = (Shape) ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }
|
问题
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很男
- 必须实现Cloneable接口
应用
- 资源优化
- 类初始化需要消化很多资源
- 一个对象多个修改者