使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理使用设计模式可以完美的解决很多问题,每种设计模式在现在中都有响应的原理来与之对应。在某些场景下,设计模式就是针对某类问题的某种通用方案。
一、设计模式简介
设计模式分为三个类别:
- 创建型模式:对象实例化的模式,创建型模式用于解耦对象实例化的过程
- 结构性模式:把类或对象结合在一起形成一个更大的结构
- 行为型模式:类和对象如何交互,及划分责任和算法
创建型模式关键点:
- 单例模式:某个类只能有一个实例,提供一个全局的访问点
- 简单工厂:一个工厂类根据传入的参量决定创建出哪一种产品类的实例
- 工厂方法:定义一个创建对象的接口,让子类决定实例化哪个类
- 抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类
- 建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造
- 原型模式:通过复制现有的实例来创建新的实例
结构性模式关键点:
- 适配器模式:将一个类的方法接口转换成客户希望的另外一个接口
- 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构
- 装饰模式:动态的给对象添加新的功能
- 代理模式:为其他对象提供一个代理以便控制这个对象的访问
- 亨元模式:通过共享技术来有效的支持大量细粒度的对象
- 外观模式:对外提供一个统一的方法,来访问子系统中的一群接口
- 桥接模式:将对象部分和它的实现部分分离,使它们都可以独立的变化
行为性模式关键点:
- 模板模式:定义一个算法结构,而将一些步骤延迟到子类实现
- 解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器
- 策略模式:定义一系列算法,把它们封装起来,并且使它们可以相互转换
- 状态模式:允许一个对象在其对象内部状态改变时改变它的行为
- 观察者模式:对象间的一对多的依赖关系
- 备忘录模式:在不破坏封装的前提下,保持对象的内部状态
- 中介者模式:用一个中介对象来封装一系列的对象交互
- 命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化
- 访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能
- 责任链模式:将请求的发送者和接受者解耦,使得多个对象都有处理这个请求的机会
- 迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构
二、常用设计模式
单例模式
单例模式三个特性:只有一个实例、自我实例化、提供全局访问点
当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式。单例模式的主要优点就是节约系统资源、提高了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,所以扩展起来有一定的困难。
/* 单例模式之懒汉式 线程不安全,延迟初始化,严格意义上不是单例模式 */ public class LazyPattern { //自己创建一个对象 public static LazyPattern lazy; //私有化构造器,放置被其他地方实例化 private LazyPattern() { } //提供一个构造器给外部使用,存在一定的线程安全问题,线程A执行到new 的时候,线程B判断hungry=true,也进到new,这样就会创建两个实例 public static LazyPattern getInstance(){ if(lazy==null){ lazy = new LazyPattern(); } return lazy; } }
懒汉式,就是实例在使用的时候才去创建,比较懒,用的时候检查有没有实例,如果没有实例,就自己新建一个。有线程安全和不安全两种写法,区别就是synchronized关键字
/* 单例模式之饿汉式 线程安全,比较常用,但容易产生垃圾,因为一开始就初始化 */ public class HungryPattern { //定义的时候就把对象创建出来 private static HungryPattern instance = new HungryPattern(); //私有构造器 private HungryPattern(){ } //外部调用构造器,一开始创建的时候就创建了对象,可以有效方式线程不安全问题 public static HungryPattern getInstance(){ return instance; } }
饿汉式,就是比较勤,实例在初始化的时候就已经创建完毕,不管有没有使用到。好处是没有线程安全的问题,坏处是浪费内存空间
/* 单例模式之双重锁模式 */ public class HungrySyronPattern { private volatile static HungrySyronPattern instance; private HungrySyronPattern() { } public static HungrySyronPattern getInstance(){ if(instance==null){ synchronized (HungrySyronPattern.class){ if(instance==null){ instance = new HungrySyronPattern(); } } } return instance; } }
双重锁模式,综合了饿汉和懒汉模式,在创建时增加了synchronized关键字,这样既保证了线程安全问题 ,也直接上锁提高了执行效率,节省 了内存空间
/* 单例模式之静态类部类 只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance , 只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。 */ public class SinglePattern { private SinglePattern() { } public static SinglePattern getInstance(){ return Inner.instance; } private static class Inner{ private static final SinglePattern instance = new SinglePattern(); } }
静态类部类,类似双重锁,实现起来更加简单
/* 单例模式之枚举单例模式 */ public enum EnumPattern { INSTANCE; public static EnumPattern getInstance(){ return EnumPattern.INSTANCE; } }
枚举,较为少见。
简单工厂模式
/* 简单工厂模式 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类 结构:1、Factory(工厂):核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象 2、Product(抽象类产品):工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象 3、ConcreteProduct(具体产品):简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。 它要实现抽象产品中声明的抽象方法(有关抽象类) */ public class SingleFactoryPattern { public static void main(String[] args) { Product product; product = Factory.GetProduct("A");//工厂类创建对象 product.MethName(); product.MethodDiff(); } } abstract class Product{ public void MethName(){ //公共方法的实现 } //生命业务抽象方法 public abstract void MethodDiff(); } class ProductA extends Product{ @Override public void MethodDiff() { //业务方法的实现 } } class ProductB extends Product{ @Override public void MethodDiff() { //业务方法的实现 } } class Factory{ public static Product GetProduct(String arg){ Product product = null; if("A".equals(arg)){ product = new ProductA(); }else if("B".equals(arg)){ product = new ProductB(); }else{ //其他情况 } return product; } }
抽象工厂模式
/* 抽象工厂模式 定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式, 属于对象创建型模式,是工厂方法模式的升级版,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是、 一种非常好的解决方式。 优缺点:1、隔离了具体类的生成,使得客户并不需要知道什么被创建,具有良好的封装性。 2、横向扩展容易。同个产品族如果需要增加多个 产品,只需要增加新的工厂类和产品类即可。 3、纵向扩展困难。如果增加新的产品组,抽象工厂类也要添加创建该产品组的对应方法, 这样一来所有的具体工厂类都要做修改了,严重违背了开闭原则。 */ public class AbstractFactoryPattern { public static void main(String[] args) { //定义出两个工厂 AbstractCreator creator1 = new Creator1(); AbstractCreator creator2 = new Creator2(); //产生A1对象 AbstractProductA a1 = creator1.createProductA(); //产生A2对象 AbstractProductA a2 = creator2.createProductA(); //产生B1对象 AbstractProductB b1 = creator1.createProductB(); //产生B2对象 AbstractProductB b2 = creator2.createProductB(); a1.doSomething(); a2.doSomething(); b1.doSomething(); b2.doSomething(); } } //产品族的抽象类A abstract class AbstractProductA { //每个产品共有的方法 public void shareMethod() { } // 每个产品相同方法,不同实现 public abstract void doSomething(); } //产品族的抽象类B abstract class AbstractProductB { //每个产品共有的方法 public void shareMethod() { } // 每个产品相同方法,不同实现 public abstract void doSomething(); } //产品族的具体实现 class ProductA1 extends AbstractProductA { public void doSomething() { System.out.println("我是产品A1"); } } //产品族的具体实现 class ProductA2 extends AbstractProductA { public void doSomething() { System.out.println("我是产品A2"); } } //产品族的具体实现 class ProductB1 extends AbstractProductB { public void doSomething() { System.out.println("我是产品B1"); } } //产品族的具体实现 class ProductB2 extends AbstractProductB { public void doSomething() { System.out.println("我是产品B2"); } } //抽象工厂类AbstractCreator,有N个产品族,在抽象工厂类中就应该有N个创建方法。 abstract class AbstractCreator { //创建A产品 public abstract AbstractProductA createProductA(); //创建B产品 public abstract AbstractProductB createProductB(); } //创建产品的具体工厂,有N个产品等级就应该有N个实现工厂类,在每个实现工厂中,实现不同产品族的生产任务。 class Creator1 extends AbstractCreator { public AbstractProductA createProductA() { return new ProductA1(); } public AbstractProductB createProductB() { return new ProductB1(); } } //创建产品的具体工厂,有N个产品等级就应该有N个实现工厂类,在每个实现工厂中,实现不同产品族的生产任务。 class Creator2 extends AbstractCreator { public AbstractProductA createProductA() { return new ProductA2(); } public AbstractProductB createProductB() { return new ProductB2(); } }
观察者模式
/* 观察者模式 定义:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。 简释:观察者一般可以看做是第三者,比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历, 这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。 成员:Subject:主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。 抽象主题提供了一个接口,可以增加和删除观察者对象 Observer:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己 ConcreteSubject:具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知 ConcreteObserver:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同 优缺点:观察者和被观察者是抽象耦合的,建立了一套触发机制 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间 如果观察者和观察目标间有循环依赖,可能导致系统崩溃 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的 */ public class Client { public static void main(String[] args) { //创建一个主题 ConcreteSubject subject = new ConcreteSubject(); //定义一个观察者 Observer observer = new ConcreteObserver(); //观察 subject.addObserver(observer); //开始活动 subject.doSomething(); } } //首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察, // 用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。 class Subject{ //观察者数组 private Vector<Observer> oVector = new Vector<>(); //增加一个观察者 public void addObserver(Observer observer) { this.oVector.add(observer); } //删除一个观察者 public void deleteObserver(Observer observer) { this.oVector.remove(observer); } //通知所有观察者 public void notifyObserver() { for(Observer observer : this.oVector) { observer.update(); } } } //具体主题 class ConcreteSubject extends Subject{ //具体业务 public void doSomething() { // super.notifyObserver(); } } //抽象观察者Observer interface Observer { public void update(); } //具体观察者 class ConcreteObserver implements Observer { @Override public void update() { System.out.println("收到消息,进行处理"); } }