20220906-重要的几种开发设计模式
重要的几种开发设计模式
1,代理模式
代理模式和日常生活中的代理是一个意思,即创建一个代理类,代替真实的业务类完成核心的业务场景。代理模式可以隐藏真实的业务类细节,代理类甚至还可以在核心业务开始和结束时,执行一些记录操作,类似于我们经常使用的
AOP。
举个例子
我们有个加载图片并展示的功能,不希望把加载图片的细节对外展示,并且希望统计每次加载图片一共花费了多少时间,这个时候我们可以考虑使用代理模式。
先看一下UML图:

再来看一下具体代码是怎么实现的:
public interface Image {
void display();
}
真正的完成核心业务的类
public class RealImage implements Image {// 实现图片接口 private String fileName; public RealImage(String fileName) throws InterruptedException { this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName) throws InterruptedException { System.out.println("Loading " + fileName); Thread.sleep((long) (Math.random()*100)); } }
代理类,除了完成主要业务逻辑,还增加了加载图片的时间统计功能。
public class ProxyImage implements Image { private RealImage realImage; private String fileName; public ProxyImage(String fileName) { this.fileName = fileName; } @Override public void display() throws InterruptedException { if (realImage == null){ long startTime = System.currentTimeMillis(); realImage = new RealImage(fileName); long endTime = System.currentTimeMillis(); long spendTime = endTime - startTime; System.out.println("加载图片花费了: " + spendTime ); } realImage.display(); } }
看一下客户端如何使用
public class ProxyPatternDemo { public static void main(String[] args) throws InterruptedException { //使用代理类 Image image = new ProxyImage("test_10mb.jpg"); //图片被加载并统计加载时间,所有细节全部被隐藏 image.display(); System.out.println(""); } }
总结
代理模式是结构型模式之一,日常使用的频率比较高,最典型的应用就是AOP,日志记录等功能。
2,工厂模式
2.1 简单工厂模式
简单工厂严格的说并不是GOF里面提到的标准的23个设计模式之一。但是日常开发中经常会用到,特别是比较小的系统。简单工厂模式就是通过一个工厂类的工厂方法创建相应的对象。
举个例子
我们希望创建三种不同类型的图表,并进行数据展示,这个时候可以考虑使用简单工厂模式。
先来看一下UML图:

来看一下代码如何实现。
先来定义一个抽象类 Chart
public abstract class Chart{ abstract void diplay(); }
分别定义三种不同的图表,均继承自Chart,这里因为篇幅原因只列出HistogramChart类
//定义柱状图图表类,体现单一职责
public class HistogramChart extends Chart { @Override void display() { //显示柱状图 } }
定义一个工厂类,通过静态工厂方法,可以创建三种不同类型的图表。
public class ChartFactory { //静态工厂方法 public static Chart createChart(String chartType){ Chart chart = null; if (chartType.equalsIgnoreCase("histogram")){ //创建柱状图 chart = new HistogramChart(); }else if (chartType.equalsIgnoreCase("pie")){ //创建饼状图 chart = new PieChart(); }else if (chartType.equalsIgnoreCase("line")){ //创建折线图 chart = new LineChart(); } return chart; } }
来看一下客户端如何使用
public class Client { public static void main(String[] args){ //通过工厂类得到想要的图表 Chart chart = ChartFactory.createChart("pie"); chart.display(); } }
总结
简单工厂模式属于创建型模式之一,用于系统比较简单,创建的种类比较少的情况,因为创建的种类如果太多的话,工厂类里面的静态工厂方法if else就太混乱了,而且所要创建的具体类和工厂类耦合太紧密。
2.2 工厂模式
工厂模式属于创建型设计模式。是日常使用比较多的一种设计模式。比如Logback,Spring等框架中都大量使用该设计模式。
简单工厂模式虽然在简单系统可以用一用,但是因为工厂类本身包含太多业务逻辑,而且如果想增加新的图表类型,需要修改工厂类等缺点。所以我们真正生产系统更多的使用工厂模式。
工厂模式为每一个类都定义一个单独的工厂类,该工厂类只负责创建单一的产品。
举个例子
通过工厂模式实现日志的创建,为不同种类的Logger分别创建不同类型的工厂。
来看一下UML图:

来看一下代码如何实现
先来定义一个Logger接口,以及LoggerFactory接口
public interface Logger { void writeLogger(); }
public interface LoggerFactory{ Logger createLogger(); }
分别定义FileLogger和FileLoggerFactory
public class FileLogger implements Logger { @Override public void writeLogger() { System.out.println("File logger"); } }
public class FileLoggerFactory implements LoggerFactory { @Override public Logger createLogger() { //不同Logger的个性初始化工作 Logger logger = new FileLogger(); return logger; } }
如果想增加新的Logger直接实现Logger和LoggerFactory就好了,看一下客户端如何使用
public class Client { public static void main(String[] args){ Logger logger; LoggerFactory loggerFactory; l loggerFactory = new FileLoggerFactory(); logger = loggerFactory.createLogger(); logger.writeLogger(); } }
总结
工厂模式是创建型模式之一,日常使用比较多,每类产品都有对应的一个工厂类。工厂模式把简单工厂的内部逻辑迁移到了客户端,当需要对功能扩展时,只需要修改客户端就OK了。
2.3 抽象工厂模式
抽象工厂提供了一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。简单来说是为需要创建多个相关联产品的场景提供了解决思路。
举个例子
很多软件都支持配置不同的颜色的主题,当切换主题时,所有按钮样式和颜色都会统一切换。
假设我们的软件只有三类按钮,分别是Button, TextField,以及ComboBox,并且我们的软件只提供两类主题,即红色主题和绿色主题。
来看一下UML :

再来看一下具体代码如何实现。
先来创建一个SkinFactory接口
public interface SkinFactory { Button createButton(); TextField createTextField(); ComboBox createComboBox(); }
通过实现SkinFactory实现绿色主题工厂
/
//红色主题工厂 public class RedSkinFactory implements SkinFactory { @Override public Button createButton() { return new RedButton(); } @Override public TextField createTextField() { return new RedTextField(); } @Override public ComboBox createComboBox() { return new RedComboBox(); } }
红色主题工厂类类似,因为篇幅原因不在单独列出。
来看一下客户端如何使用
public class Client { public static void main(String[] args){ //通过切换不同工厂,可以达到所有按钮颜色都变换的效果 SkinFactory skinFactory = new RedSkinFactory(); Button button = skinFactory.createButton(); ComboBox comboBox = skinFactory.createComboBox(); TextField textField = skinFactory.createTextField(); button.display(); comboBox.display(); textField.display(); } }
总结
抽象工厂是创建模式之一,抽象工厂适合创建一系列有相关性的多个产品,虽然抽象工厂在扩展一个主题的按钮时灵活方便,但是也有一定缺点, 最大缺点就是想单独增加一个不属于任何主题的按钮时比较麻烦,比如上面的例子中,如果想增加一个关闭按钮,这个按钮即不属于红色主题也不属于绿主题,那么改动起来就比较复杂。
工厂模式三兄弟:简单工厂适合逻辑简单,创建产品数量很少的场景,工厂模式适合创建独立的产品,抽象工厂适合创建一系列有相关性的多个产品。
3 单例模式
单例模式顾名思义,就是保证系统中一个类只有一个唯一的对象实例。实际开发过程中经常会碰到这种需求。
单例模式有两种实现方式,一种称为饿汉式,一种称为懒汉式。
先来看一下UML图:

饿汉式
之所以称为饿汉,是因为该种实现方式是在类被加载的时候就初始化对象,而且是通过JVM的类加载机制保证系统中只有一个类实例。
先看饿汉式的完整代码
public class SingleObject { //create an object of SingleObject private static SingleObject instance = new SingleObject(); //make the constuctor private so that this class cannot be instantiated private SingleObject(){} public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("I'm eager"); } }
饿汉式的关键代码在第一行,通过static关键字修饰了instance变量了,这样在类被加载的时候就会初始化SingleObject。
懒汉式
所谓的懒汉式就是在需要的时候在创建对象,懒汉式比较复杂,需要通过锁加双重检查机制来保障多线程环境下,只有一个实例被创建。完整代码如下
public class LazySingleObject { private volatile static LazySingleObject instance = null; private LazySingleObject() {} public static LazySingleObject getInstance(){ //第一重检查 if (instance == null){ synchronized (LazySingleObject.class){ //第二重检查 if (instance == null){ instance = new LazySingleObject(); } } } return instance; } public void showMessage(){ System.out.println("I'm lazy"); } }
这样使用单例模式
SingleObject object = SingleObject.getInstance();//show the messageobject.showMessage();
总结
单例模式也是创建型模式之一,实现该模式的时候都要把实例变量设置为静态的,其次构造函数都要变成私有的,防止在外部被引用并创建对象。饿汉式实现起来比较容易,但是初始化的实例对象比较耗时和占用资源的话,在系统启动时会比较浪费时间和资源;相反懒汉式则是在需要的时候进行初始化,不会占用系统启动资源,但是实现起来比较麻烦,特别是在多线程的场景下,除非有特殊要求,否则建议使用饿汉式。
4 委派模式
定义
委派模式,是一种面向对象的设计模式,是一种特殊的静态代理,可以理解为全权代理,代理模式注重的是对原始被代理的增强,委派注重的是交给委派对象拿到委派之后的结果。委派属于行为型设计模式。
委派的角色(类似中介)
- 抽象任务角色(Task)定义一个抽象解耦他有若干实现类
- 委派者角色(Delegate): 负责在各个具体角色实例之间做出角色,判断并调用具体实现方法
- 具体任务角色(Concrete): 真正执行任务的角色
/
//具体任务类抽象 public interface IEmployee { void doing(String task); } //具体任务类A public class EmployeeA implements IEmployee{ private String role = "程序员"; @Override public void doing(String task) { System.out.println("开始编程"); } } //具体任务类B public class EmployeeB implements IEmployee{ private String role = "设计师"; @Override public void doing(String task) { System.out.println("开始画稿"); } } //委派角色 负载分发任务 和 执行 public class Leader implements IEmployee{ private Map<String, IEmployee> employeeMap = new HashMap<>(); public Leader(){ employeeMap.put("coding", new EmployeeA()); employeeMap.put("design", new EmployeeB()); } //这里使用策略模式,拿到具体的一个方案,然后执行 @Override public void doing(String task) { if(!employeeMap.containsKey(task)) { System.out.println(task + ",sorry能力有限另寻高明"); }else{ employeeMap.get(task).doing(task); } } } //调用者持有委派角色,直接从委派角色拿到结果,对执行过程完全透明 public class Boss { public void command(String task, Leader leader){ leader.doing(task); } public static void main(String[] args) { Boss boss = new Boss(); Leader leader = new Leader(); boss.command("coding", leader); } }
委派模式优缺点:
优点:将大的任务通过任务职责细分到不同的委派角色 每个委派角色负责执行各个子任务,边界更明确
缺点: 代码实现更复杂,类的关系也更复杂,任务复杂的情况下可能出现多重委派,导致代码结构紊乱
5 策略模式
策略模式常用于一个对象的行为在运行时有多个不同的策略和算法。
举个例子
可以通过实现一个加、减、乘三种不用策略的运算,通过该示例演示策略模式。
先来看一下UML:

来看一下代码如何实现
先来定义一个策略接口
public interface Strategy { int doOperation(int num1, int num2); }
分别定义具体的加、减、乘策略,实现策略接口
定义加法策略
public class OperationAdd implements Strategy { @Override public int doOperation(int num1, int num2) { return num1 + num2; } }
减法和乘法策略大同小异,篇幅原因掠过。
定义一个Context方法,根据不同策略执行不同的行为
public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } //执行不同的策略 public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1,num2); } }
来看一下客户端如何使用
public class StrategyPatternDemo { public static void main(String[] args){ Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10,5)); context = new Context(new OperationSubstract()); System.out.println("10 - 5 = " + context.executeStrategy(10,5)); context = new Context(new OperationMultiply()); System.out.println("10 * 5 = " + context.executeStrategy(10,5)); } }
总结
策略模式是行为模式之一,策略模式是用来封装算法的,实践过程中,不仅可以封装算法,任何类型的规则都可以封装。
6 原型模式
原型模式其实很简单,就是利用Java提供的Clonable接口,实现对象的复制。之所以称之为原型模式,是因为我们复制的时候要有一个原型或者模型。该模式经常用于所要创建的对象比较复杂的时候,我们可以直接基于现有的对象复制一个出来直接使用。使用原型模式的时候要考虑Java的深复制和浅复制,要进行深复制的时候建议通过序列化的方式实现。
举个例子
通过一个图形缓存器来演示原型模式的使用。
先来看一下UML :

下面给出一个简单的图形缓存器来演示原型模式(浅复制)。
public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); //篇幅原因,此处省略get,set方法 //注意这段代码,关键就都在这里了。 @Override protected Object clone() { Object clone = null; try{ clone = super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return clone; } }
定义一个Circle实现Shape
public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override void draw() { System.out.println("Inside Circle::draw() method"); } }
定义一个图形缓存器,里面存储创建好的图形,当客户端需要一个新的图形时,直接从图形缓存器复制一个出来。
public class ShapeCache { private static Map<String,Shape> shapeMap = new HashMap<>(); public static Shape getShape(String shapeId){ Shape cacheShape = shapeMap.get(shapeId); // 通过clone的方式返回一个和现有Circle一模一样的新Circle return (Shape) cacheShape.clone(); } //创建一个新的Circle,并放进缓存器 public static void loadCache(){ Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); } }
客户端这样使用
public class PrototypePatternDemo { public static void main(String[] args){ //创建新的Cirlce并放进缓存器 ShapeCache.loadCache(); //通过clone的方式拿到一个和原来一样的新Cirlce Shape cloneShape = ShapeCache.getShape("1"); System.out.println("Shape : " + cloneShape.getType() + "ID : " + cloneShape.getId() ); } }
总结
原型模式很简单,是创建型模式之一,只要记住原型模式就是利用Cloneable实现对象的复制就够了。
7 模板模式
模板方法通常用来定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法可以在不改变一个算法的结构的情况下,就可以重新定义该算法的某些特定步骤。
举个例子
我们来实现一个竞技类的电子游戏,大部分的竞技游戏的流程基本上都包含如下过程 初始化游戏,启动游戏,游戏结束这几个步骤,但是不同游戏的初始化和启动方法不一样,我们可以考虑使用模板方法来实现。
来看一下UML:

看一下具体代码如何实现
先来定义一个抽象的游戏类Game,里面定义模板方法
public abstract class Game { abstract void init(); abstract void startPlay(); abstract void endPlay(); //template method public final void play(){ init(); startPlay(); endPlay(); } }
分别定义表示足球游戏的类Football和表示篮球游戏的类BasketBall;两个类全部继承自抽象类Game
public class Football extends Game { @Override void init() { System.out.println("Football Game init"); } @Override void startPlay() { System.out.println("Football Game Start"); } @Override void endPlay() { System.out.println("Football Game End"); } }
BasketBall和Football类似,因篇幅原因不再列出。
来看一下客户端如何使用
public class TemplatePatternDemo { public static void main(String[] args){ Game basketBall = new BasketBall(); basketBall.play(); Game football = new Football(); football.play(); } }
总结
模板方法是行为模式之一,模板方法通过把不变的行为搬移到父类,去除子类中的重复代码,使其代码结构更精简。
浙公网安备 33010602011771号