关于设计模式
在软件开发的历程中,正是无数前辈们本着追求完美的架构设计和代码设计的初心。经过无数前辈们的探索和努力,逐渐形成了一系列的设计原则和设计模式。
对于Java语言来说,通常包含有6大设计原则和23中设计模式。这些都是前辈们对于开发思想的结晶。我们学习和理解这些设计原则和设计模式,深入掌握其实现原理和使用场景,能够更好的设计我们的系统架构。编写出具有高性能、高并发、高可用、高可扩展性和高可维护性的代码。
在Java的常见的23种设计模式中,大体上可以分为创建型模式、结构型模式和行为型模式三大类。本系列文章就对Java中的23种设计模式进行汇总,方便记录和查阅。
六大设计原则
开闭原则(Open Close Principle)
- 开闭原则:对扩展开放,对修改关闭
- 程序进行扩展的时候,不能修改原有的代码, 实现一个热插拔的效果
- 为了使程序扩展性好,易于维护和升级:需要使用接口和抽象类
里氏代换原则(Liskov Substitution Principle)
- 里氏代换原则:任何基类可以出现的地方,子类一定可以出现
LSP
是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受影响时,基类才能真正被复用,衍生类也能够在基类的基础上增加新的行为- 里氏代换原则是对实现抽象化的具体步骤的规范:
- 里氏代换原则是对开闭原则的补充
- 实现开闭原则的关键步骤就是抽象化
- 基类与子类的继承关系就是抽象化的具体实现
依赖倒转原则(Dependence Inversion Principle)
- 依赖倒转原则:针对接口编程,依赖于抽象而不依赖于具体
- 依赖倒转原则是开闭原则的基础
接口隔离原则(Interface Segregation Principle)
- 接口隔离原则:使用多个隔离的接口,比使用单个接口要好,降低类之间的耦合度
- 从接口隔离原则可以看出:设计模式就是一个软件的设计思想
- 从大型软件架构出发,为了升级和维护方便:降低依赖,降低耦合
迪米特法则(最少知道原则)(Demeter Principle)
- 迪米特法则:最少知道原则,一个实体应当尽量少的与其它实体发生相互作用,使得功能模块相互独立
合成复用原则(Composite Reuse Principle)
- 合成复用原则:尽量使用合成或者聚合的方式,而不是使用继承
创造型模式
单例模式
看几个单例对象的示例代码,其中有些代码是线程安全的,有些则不是线程安全的,需要大家细细品味。
- 例一:SingletonExample1
- 这个类是懒汉模式,并且是线程不安全的
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 19:48
* @Descripetion: 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程不安全的
**/
public class SingletonExample1 {
private SingletonExample1(){
}
private static SingletonExample1 instance = null;
private static SingletonExample1 getInstance(){
//多个线程同时调用,可能会创建多个对象
if (instance == null){
instance = new SingletonExample1();
}
return instance;
}
}
- 例二:SingletonExample2
- 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 19:52
* @Descripetion: 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
**/
public class SingletonExample2 {
private SingletonExample2(){}
private static SingletonExample2 instance = new SingletonExample2();
public static SingletonExample2 getInstance(){
return instance;
}
}
- 例三:SingletonExample3
- 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 19:54
* @Descripetion: 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写 法不推荐
**/
public class SingletonExample3 {
private SingletonExample3(){}
private static SingletonExample3 instance = null;
public static synchronized SingletonExample3 getInstance(){
if (instance == null){
instance = new SingletonExample3();
}
return instance;
}
}
- 例四:SingletonExample4
- 懒汉模式(双重锁同步锁单例模式),单例实例在第一次使用的时候进行创建,但是,这个类不是线程安全的!!!!!
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 19:57
* @Descripetion: 懒汉模式(双重锁同步锁单例模式)
* 单例实例在第一次使用的时候进行创建,这个类不是线程安全的
**/
public class SingletonExample4 {
private SingletonExample4(){}
private static SingletonExample4 instance = null;
public static SingletonExample4 getInstance(){
if (instance == null){
synchronized (SingletonExample4.class){
if (instance == null){
instance = new SingletonExample4();
}
}
}
return instance;
}
}
-
线程不安全分析如下:
-
当执行
instance = new SingletonExample4();
这行代码时,CPU会执行如下指令:- 1.
memory = allocate()
分配对象的内存空间 - 2.
ctorInstance()
初始化对象 - 3.
instance = memory
设置instance
指向刚分配的内存
- 1.
-
单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
-
指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。
-
如果发生了
JVM
和CPU
优化,发生重排序时,可能会按照下面的顺序执行:- 1.
memory = allocate()
分配对象的内存空间 - 3.
instance = memory
设置instance
指向刚分配的内存 - 2.
ctorInstance()
初始化对象
- 1.
-
假设目前有两个线程A和B同时执行
getInstance()
方法,A线程执行到instance = new SingletonExample4();
B线程刚执行到第一个if (instance == null){}
处, -
如果按照
1.3.2
的顺序,假设线程A执行到3.instance = memory
设置instance
指向刚分配的内存,此时,线程B判断instance
已经有值,就会直接return instance;
-
而实际上,线程A还未执行
2.ctorInstance()
初始化对象,也就是说线程B拿到的instance
对象 还未进行初始化,这个未初始化的instance
对象一旦被线程B使用,就会出现问题。 -
例五:SingletonExample5
-
懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类是线程安全的,使用的是
volatile +
双重检测机制来禁止指令重排达到线程安全
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:06
* @Descripetion: 懒汉模式(双重锁同步锁单例模式)
* 单例实例在第一次使用的时候进行创建,这个类是线程安全的
**/
public class SingletonExample5 {
private SingletonExample5(){}
//单例对象 volatile + 双重检测机制来禁止指令重排
private volatile static SingletonExample5 instance = null;
public static SingletonExample5 getInstance(){
if (instance == null){
synchronized (SingletonExample5.class){
if (instance == null){
return instance = new SingletonExample5();
}
}
}
return instance;
}
}
- 例六:SingletonExample6
- 饿汉模式,单例实例在类装载的时候(使用静态代码块)进行创建,是线程安全的
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:09
* @Descripetion: 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
**/
public class SingletonExample6 {
private SingletonExample6(){}
private static SingletonExample6 instance = null;
static {
instance = new SingletonExample6();
}
public static SingletonExample6 getInstance(){
return instance;
}
}
- 例七:SingletonExample7
- 枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
package com.cz.Creativemodel;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:11
* @Descripetion: 枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
**/
public class SingletonExample7 {
private SingletonExample7(){}
public static SingletonExample7 getInstance(){
return Singleton.INSTANCE.getInstance();
}
private enum Singleton{
INSTANCE;
private SingletonExample7 singleton;
//JVM保证这个方法绝对只调用一次
Singleton(){
singleton = new SingletonExample7();
}
public SingletonExample7 getInstance(){
return singleton;
}
}
}
抽象工厂模式
一、概述:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
二、为何使用
工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。
为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new
,我们经常要根据类Class生成实例对象,如A a=new A()
工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量
三、实用性
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
四、参与者
AbstractFactory
声明一个创建抽象产品对象的操作接口。ConcreteFactory
实现创建具体产品对象的操作。AbstractProduct
为一类产品对象声明一个接口。ConcreteProduct
定义一个将被相应的具体工厂创建的产品对象。 实现AbstractProduct
接口。Client
仅使用由AbstractFactory
和AbstractProduct
类声明的接口
五、类图
六、示例
AbstractFactory
:定义抽象工程类IAnimalFactory
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:28
* @Descripetion: 这个接口就是类图中标识的AbstractFactory抽象工厂
**/
public interface IAnimalFactory {
/**
* 定义创建Icat接口实例的方法
* @return
*/
ICat createCat();
/**
* 定义创建IDog接口实例的方法
* @return
*/
IDog createDog();
}
ConcreteFactory
:创建抽象工厂类的两个实现类,WhiteAnimalFactory
和BlackAnimalFactory
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:31
* @Descripetion: IAnimalFactory抽象工厂的实现类
**/
public class WhiteAnimalFactory implements IAnimalFactory {
@Override
public ICat createCat() {
return new WhiteCat();
}
@Override
public IDog createDog() {
return new WhiteDog();
}
}
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:32
* @Descripetion: IAnimalFactory抽象工厂的实现类
**/
public class BlackAnimalFactory implements IAnimalFactory {
@Override
public ICat createCat() {
return new BlackCat();
}
@Override
public IDog createDog() {
return new BlackDog();
}
}
AbstractProduct
:定义抽象工厂中要生产的抽象产品接口ICat
和IDog
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:34
* @Descripetion: 类图中定义的AbstractProduct
* 指定工厂生产的产品
**/
public interface ICat {
/**
* 定义方法
*/
void eat();
}
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:36
* @Descripetion: 类图中定义的AbstractProduct
* 指定工厂生产的产品
**/
public interface IDog {
/**
*定义方法
*/
void eat();
}
ConcreteProduct
:创建产品的实现类BlackCat
、BlackDog
、WhiteCat
、WhiteDog
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:37
* @Descripetion: ICat接口的实现类
**/
public class BlackCat implements ICat {
@Override
public void eat() {
System.out.println("The black cat is eating!");
}
}
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:38
* @Descripetion: IDog的实现类
**/
public class BlackDog implements IDog {
@Override
public void eat() {
System.out.println("The black dog is eating");
}
}
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:39
* @Descripetion: ICat的实现类
**/
public class WhiteCat implements ICat {
@Override
public void eat() {
System.out.println("The white cat is eating!");
}
}
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:39
* @Descripetion: IDog的实现类
**/
public class WhiteDog implements IDog {
@Override
public void eat() {
System.out.println("The white dog is eating!");
}
}
- Client:定义一个测试类Test
package com.cz.Creativemodel.abstractfactory;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:43
* @Descripetion: 测试类
**/
public class Test {
public static void main(String[] args) {
IAnimalFactory blackAnimalFactory = new BlackAnimalFactory();
ICat blackCat = blackAnimalFactory.createCat();
blackCat.eat();
IDog blackDog = blackAnimalFactory.createDog();
blackDog.eat();
IAnimalFactory whiteAnimalFactory = new WhiteAnimalFactory();
ICat whiteCat = whiteAnimalFactory.createCat();
whiteCat.eat();
IDog whiteDog = whiteAnimalFactory.createDog();
whiteDog.eat();
}
}
- 运行结果:
七、总结
由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化
工厂方法
一、概述
定义一个用于创建对象的接口,让子类决定实例化哪一个类。FactoryMethod
使一个类的实例化延迟到其子类。
二、适用性
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
三、参与者
Product
定义工厂方法所创建的对象的接口。ConcreteProduct
实现Product
接口。Creator
声明工厂方法,该方法返回一个Product
类型的对象。Creator
也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct
对象。 可以调用工厂方法以创建一个Product
对象。ConcreteCreator
重定义工厂方法以返回一个ConcreteProduct
实例。
四、类图
五、示例
- Product
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:52
* @Descripetion: 定义一个接口Wo
**/
public interface Work {
/**
* 定义方法
*/
void doWork();
}
- ConcreteProduct
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:52
* @Descripetion: Work接口的具体实现类
**/
public class TeacherWork implements Work {
@Override
public void doWork() {
System.out.println("老师审批作业!");
}
}
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:53
* @Descripetion: Work接口的具体实现类
**/
public class StudentWork implements Work {
@Override
public void doWork() {
System.out.println("学生做作业!");
}
}
- Creator
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:54
* @Descripetion: 抽象工厂接口
**/
public interface IWorkFactory {
/**
* 定义获取Work实例对象的方法
* @return
*/
Work getWork();
}
- ConcreteCreator
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:55
* @Descripetion: IWorkFactory工厂的实现类
**/
public class StudentWorkFactory implements IWorkFactory {
@Override
public Work getWork() {
return new StudentWork();
}
}
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 20:56
* @Descripetion: IWorkFactory工厂的实现类
**/
public class TeacherWorkFactory implements IWorkFactory {
@Override
public Work getWork() {
return new TeacherWork();
}
}
- Test
package com.cz.Creativemodel.factorymethod;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:00
* @Descripetion: 测试类
**/
public class Test {
public static void main(String[] args) {
IWorkFactory studentWorkFactory = new StudentWorkFactory();
studentWorkFactory.getWork().doWork();
IWorkFactory teacherWorkFactory = new TeacherWorkFactory();
teacherWorkFactory.getWork().doWork();
}
}
- 运行结果:
建造者模式
一、概述
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
二、适用性
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
三、参与者
Builder
为创建一个Product
对象的各个部件指定抽象接口。ConcreteBuilder
实现Builder
的接口以构造和装配该产品的各个部件。定义并明确它所创建的表示。提供一个检索产品的接口。Director
构造一个使用Builder
接口的对象。Product
表示被构造的复杂对象。ConcreteBuilder
创建该产品的内部表示并定义它的装配过程。包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
四、类图
五、示例
- Builder
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:09
* @Descripetion: Person对象的构造接口
**/
public interface PersonBuilder {
void buildHead();
void buildBody();
void buildFoot();
Person buildPerson();
}
- ConcreteBuilder
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:12
* @Descripetion: Person对象的构造器
**/
public class ManBuilder implements PersonBuilder {
Person person;
public ManBuilder(){
person = new Man();
}
@Override
public void buildHead() {
person.setHead("建造男人的头");
}
@Override
public void buildBody() {
person.setBody("建造男人的身体");
}
@Override
public void buildFoot() {
person.setFoot("建造男人的脚");
}
@Override
public Person buildPerson() {
return person;
}
}
- Director
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:15
* @Descripetion: Person对象的整体构造器
**/
public class PersonDirector {
public Person constructPerson(PersonBuilder pb){
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.buildPerson();
}
}
- Product
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:17
* @Descripetion: Person对象
**/
public class Person {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot = foot;
}
}
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:18
* @Descripetion: 创建一个男人类继承Person
**/
public class Man extends Person {
}
- Test
package com.cz.Creativemodel.builder;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:19
* @Descripetion: 描述
**/
public class Test {
public static void main(String[] args) {
PersonDirector pd = new PersonDirector();
Person person = pd.constructPerson(new ManBuilder());
System.out.println(person.getHead());
System.out.println(person.getBody());
System.out.println(person.getFoot());
}
}
- 运行结果:
原型模式
一、概述
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二、适用性
- 当一个系统应该独立于它的产品创建、构成和表示时。
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。
建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
三、参与者
Prototype
声明一个克隆自身的接口。ConcretePrototype
实现一个克隆自身的操作。Client
让一个原型克隆自身从而创建一个新的对象。
四、类图
五、示例
- Prototype
package com.cz.Creativemodel.prototype;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:24
* @Descripetion: 原型类,实现Cloneable接口
**/
public class Prototype implements Cloneable {
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public Object clone(){
try {
return super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
- ConcretePrototype
package com.cz.Creativemodel.prototype;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:26
* @Descripetion: 原型类的子类,用于构建原型
**/
public class ConcretePrototype extends Prototype {
public ConcretePrototype(String name){
setName(name);
}
}
- Client
package com.cz.Creativemodel.prototype;
/**
* @Author: zhang
* @Date: 2021/3/29 - 21:28
* @Descripetion: 测试类
**/
public class Test {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("prototype");
Prototype prototype1 = (Prototype)prototype.clone();
System.out.println(prototype.getName());
System.out.println(prototype1.getName());
}
}
- 运行结果:
创造型模式就讲完了;结构性模式和行为模式会在接下来的文章写