前言

有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文:

  • 面向接口编程,而不是面向实现。这个很重要,也是优雅的、可扩展的代码的第一步,这就不需要多说了吧。

  • 职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。

  • 对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。

创建型模式比较简单,但是会比较没有意思,结构型和行为型比较有意思

每个代理模式的代码都必须自己手动完成一遍。

创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

工厂模式分为简单工厂模式,工厂模式,抽象工厂模式

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。本质就是使用工厂方法代替new操作。

简单工厂模式

public class FoodFactory {public static Food makeFood(String name) {if (name.equals("兰州拉面")) {Food noodle = new LanZhouNoodle();System.out.println("兰州拉面"+noodle+"出锅啦");return noodle;} else if (name.equals("黄焖鸡")) {Food chicken = new HuangMenChicken();System.out.println("黄焖鸡"+ chicken +"出锅啦");return chicken;} else {System.out.println("不知道你做的什么哦~");return null;}}
}

其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。

public class Cook {public static void main(String[] args) {Food food = FoodFactory.makeFood("黄焖鸡");FoodFactory.makeFood("jaja");}
}

简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。

我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

在此例中可以看出,Cook 类在使用 FoodFactory 时就不需要 new 任何一个对象,这就是简单工厂模式的好处,封装了 new 的部分,做到的代码易用性。

工厂模式

简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。

public interface FoodFactory {Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {@Overridepublic Food makeFood(String name) {if (name.equals("A")) {return new ChineseFoodA();} else if (name.equals("B")) {return new ChineseFoodB();} else {return null;}}
}
public class AmericanFoodFactory implements FoodFactory {@Overridepublic Food makeFood(String name) {if (name.equals("A")) {return new AmericanFoodA();} else if (name.equals("B")) {return new AmericanFoodB();} else {return null;}}
}

其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。

客户端调用:

public class APP {public static void main(String[] args) {// 先选择一个具体的工厂FoodFactory factory = new ChineseFoodFactory();// 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象Food food = factory.makeFood("A");}
}

虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。

第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。

核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量

  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

AbstractFactory 声明一个创建抽象产品对象的操作接口。

ConcreteFactory 实现创建具体产品对象的操作。

AbstractProduct 为一类产品对象声明一个接口。

ConcreteProduct 定义一个将被相应的具体工厂创建的产品对象。 实现AbstractProduct接口。

Client 仅使用由AbstractFactory和AbstractProduct类声明的接口

示例:

AbstractFactory:定义抽象工程类IAnimalFactory

package  com.pizilei.design.abstractfactory;
/**
*  这个接口就是类图中标识的
*  AbstractFactory抽象工厂
*  @author  pizilei
*
*/
public  interface  IAnimalFactory  {/***  定义创建Icat接口实例的方法*  @return*/ICat  createCat();/***  定义创建IDog接口实例的方法*  @return*/IDog  createDog();
}

ConcreteFactory创建抽象工厂类的两个实现类,WhiteAnimalFactoryBlackAnimalFactory

package  com.pizilei.design.abstractfactory;
/**
*  IAnimalFactory抽象工厂的实现类
*  @author  pizilei
*
*/
public  class  WhiteAnimalFactory  implements  IAnimalFactory  {@Overridepublic  ICat  createCat()  {return  new  WhiteCat();}@Overridepublic  IDog  createDog()  {return  new  WhiteDog();}}package  com.pizilei.design.abstractfactory;
/**
*  IAnimalFactory抽象工厂的实现类
*  @author  pizilei
*/
public  class  BlackAnimalFactory  implements  IAnimalFactory  {@Overridepublic  ICat  createCat()  {return  new  BlackCat();}@Overridepublic  IDog  createDog()  {return  new  BlackDog();}}

AbstractProduct定义抽象工厂中要生产的抽象产品接口ICatIDog

package  com.pizilei.design.abstractfactory;
/**
*  类图中定义的AbstractProduct
*  指定工厂生产的产品
*  @author  pizilei
*
*/
public  interface  ICat  {/***  定义方法*/void  eat();
}package  com.pizilei.design.abstractfactory;
/**
*  类图中定义的AbstractProduct
*  指定工厂生产的产品
*  @author  pizilei
*
*/
public  interface  IDog  {/***  定义方法*/void  eat();
}

ConcreteProduct创建产品的实现类BlackCatBlackDogWhiteCatWhiteDog

package  com.pizilei.design.abstractfactory;
/**
*  ICat接口的实现类
*  @author  pizilei
*
*/
public  class  BlackCat  implements  ICat  {
@Overridepublic  void  eat()  {System.out.println("The  black  cat  is  eating!");}
}package  com.pizilei.design.abstractfactory;/**
*  IDog的实现类
*  @author  pizilei
*/
public  class  BlackDog  implements  IDog  {
@Overridepublic  void  eat()  {System.out.println("The  black  dog  is  eating");}
}package  com.pizilei.design.abstractfactory;
/**
*  ICat的实现类
*  @author  pizilei
*
*/
public  class  WhiteCat  implements  ICat  {
@Overridepublic  void  eat()  {System.out.println("The  white  cat  is  eating!");}
}package  com.pizilei.design.abstractfactory;
/**
*  IDog的实现类
*  @author  binghe
*
*/
public  class  WhiteDog  implements  IDog  {
@Overridepublic  void  eat()  {System.out.println("The  white  dog  is  eating!");}}

Client:定义一个测试类Test


/**
*  测试类
*  @author  pizilei
*
*/
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();}
}

输出结果:

The  black  cat  is  eating!
The  black  dog  is  eating
The  white  cat  is  eating!
The  white  dog  is  eating!

由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化

单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。

getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。

特点

  • 类构造器私有
  • 持有自己类型的属性
  • 对外提供获取实例的静态方法

饿汉式写法

public class Singleton {  private static Singleton instance = new Singleton();  private Singleton (){}  public static Singleton getInstance() {  return instance;  }
}

弊端:因为类加载的时候就会创建对象,所以有的时候还不需要使用对象,就会创建对象,造成内存的浪费;

饱汉模式最容易出错:

public class Singleton {// 首先,也是先堵死 new Singleton() 这条路private Singleton() {}// 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的private static volatile Singleton instance = null;public static Singleton getInstance() {if (instance == null) {// 加锁synchronized (Singleton.class) {// 这一次判断也是必须的,不然会有并发问题if (instance == null) {instance = new Singleton();}}}return instance;}
}

双重检查,指的是两次检查 instance 是否为 null。

volatile 在这里是需要的,希望能引起读者的关注。

很多人不知道怎么写,直接就在 getInstance() 方法签名上加上 synchronized,这就不多说了,性能太差。

嵌套类最经典,以后大家就用它吧:

public class Singleton {private Singleton() {}// 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性private static class Holder {private static Singleton instance = new Singleton();}public static Singleton getInstance() {return Holder.instance;}
}

注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。

最后,我们说一下枚举,枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。

TODO:

建造者模式

原型模式

结构型模式

前面创建型模式介绍了创建对象的一些设计模式,这节介绍的结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。

代理模式

第一个要介绍的代理模式是最常使用的模式之一了,用一个代理来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑。

既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。

理解代理这个词,这个模式其实就简单了。 下面上代码理解。 代理接口:

//要有一个代理接口让实现类和代理实现类来实现。
public interface FoodService {Food makeChicken();
}

被代理的实现类:

public class FoodServiceImpl implements FoodService {@Overridepublic Food makeChicken() {Food f = new Chicken();f.setChicken("1kg");f.setSpicy("1g");f.setSalt("3g");System.out.println("鸡肉加好佐料了");return f;}
}

被代理实现类就只需要做自己该做的事情就好了,不需要管别的。

代理实现类:

public class FoodServiceProxy implements FoodService {// 内部一定要有一个真实的实现类,当然也可以通过构造方法注入private FoodService foodService = new FoodServiceImpl();@Overridepublic Food makeChicken() {System.out.println("开始制作鸡肉");// 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,// 代理只是在核心代码前后做些“无足轻重”的事情Food food = foodService.makeChicken();System.out.println("鸡肉制作完成啦,加点胡椒粉");food.addCondiment("pepper");System.out.println("上锅咯");return food;}
}

客户端调用,注意,我们要用代理来实例化接口:

// 这里用代理类来实例化
FoodService foodService = new FoodServiceProxy();
foodService.makeChicken();

所谓代理模式,就是对被代理方法包装或者叫增强, 在面向切面编程(AOP)中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before、@After、@Around 中的代码逻辑动态添加到代理中。

待续。。。

行为型模式

模板模式

在含有继承结构的代码中,模板方法模式是非常常用的。

父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现

模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。

好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好

缺点: 每一个不同的实现都需要一个子类来实现,导致类个数增加,使系统更加庞大

模板模式的关键点:

    1、使用抽象类定义模板类,并在其中定义所有的基本方法、模板方法,钩子方法,不限数量,以实现功能逻辑为主。其中基本方法使用final修饰,其中要调用基本方法和钩子方法,基本方法和钩子方法可以使用protected修饰,表明可被子类修改。

    2、定义实现抽象类的子类,重写其中的模板方法,甚至钩子方法,完善具体的逻辑。

  使用场景:

    1、在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。

    2、程序主框架相同,细节不同的情况下,也可以使用模板方法。

架构方法介绍

模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。其主要分为两大类:模版方法和基本方法,而基本方法又分为:抽象方法(Abstract Method),具体方法(Concrete Method),钩子方法(Hook Method)。

四种方法的基本定义(前提:在抽象类中定义):

(1)抽象方法:由抽象类声明,由具体子类实现,并以abstract关键字进行标识。

(2)具体方法:由抽象类声明并且实现,子类并不实现或者做覆盖操作。其实质就是普遍适用的方法,不需要子类来实现。

(3)钩子方法:由抽象类声明并且实现,子类也可以选择加以扩展。通常抽象类会给出一个空的钩子方法,也就是没有实现的扩展。它和具体方法在代码上没有区别,不过是一种意识的区别;而它和抽象方法有时候也是没有区别的,就是在子类都需要将其实现的时候。而不同的是抽象方法必须实现,而钩子方法可以不实现。也就是说钩子方法为你在实现某一个抽象类的时候提供了可选项,相当于预先提供了一个默认配置。

(4)模板方法:定义了一个方法,其中定义了整个逻辑的基本骨架。

public abstract class AbstractTemplate {// 这就是模板方法public void templateMethod() {init();apply(); // 这个是重点end(); // 可以作为钩子方法}//这是具体方法protected void init() {System.out.println("init 抽象层已经实现,子类也可以选择覆写");}// 这是抽象方法,留给子类实现protected abstract void apply();//这是钩子方法,可定义一个默认操作,或者为空protected void end() {}
}

深入浅出23种设计模式(最全面)相关推荐

  1. 深入浅出Java 23种设计模式,最全PDF版本终于开放下载了!!(文末有福利)

    写在前面 在「 冰河技术 」微信公众号中[设计模式专题]更新完毕已有一段时间了.不少小伙伴在我微信上留言说:冰河,你能不能把[设计模式专题]的文章汇总成PDF文档呢?一直没有时间整理,最近在公众号后台 ...

  2. 非常全的23种设计模式

    非常全的23种设计模式详解,收藏了 2016年09月12日 14:21:34 阅读数:3382 源博文出处:http://blog.csdn.net/lovelion/article/details/ ...

  3. Java开发中的23种设计模式详解(转)

    设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  4. 23 种设计模式实战 pdf(很全)

    今天分享一份学习资料:23 种设计模式实战教程.pdf,助你快速上手设计模式,写出各种高端代码,文末附下载地址. 设计模式一般分为三大类: 实战教程: 教程共 96 页PDF,太全了!纯粉丝福利,非广 ...

  5. 从追MM谈23种设计模式

    从追MM谈Java的23种设计模式 1.FACTORY-追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说&quo ...

  6. 【设计模式】Java 23种设计模式对比总结

    一.设计模式的分类 创建型模式,共五种(1-5):工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种(6-12):适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组 ...

  7. java 的23种设计模式 单例模式

    23种设计模式友情链接: 点击打开链接 单例模式: A.饿汉式单例模式 具体步骤: 1.声明一个私有的静态的最终的本类类型的对象并实例化 private static final Person ins ...

  8. 23种设计模式C++实现UML+源码汇总

    设计模式-汇总 代码开源仓库地址 23种设计模式C++实现 C++中的开闭原则使用C++多态功能实现附源码 C++基于多态实现依赖颠倒原则附源码 C++ 的静态成员变量为什么一定要在类外定义 23种设 ...

  9. 23种设计模式C++源码与UML实现--外观模式

    外观模式 facade模式也叫外观模式,是由GoF提出的23种设计模式中的一种,facade模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单界面.这个一致的简单的界面被称为fac ...

  10. 23种设计模式C++源码与UML实现--建造者模式

    建造者模式 代码仓库地址:建造者模式 Builder模式也叫建造者模式或者生成器模式,是由GoF提出的23种设计模式中的一种.Builder模式是一种对象创建模式之一,用来隐藏复合对象的创建过程.他把 ...

最新文章

  1. NoSQL生态系统——一致性RWN协议,向量时钟,gossip协议监测故障
  2. mRemoteNG 远程连接工具(开源)
  3. 网络编程基础之C/S架构和TCP/IP协议
  4. Hadoop 1.2.1 集群安装一
  5. lsattr/chattr
  6. 笔记47-徐 数据库引擎中基于行版本控制的隔离级别
  7. php矢量瓦片,矢量瓦片 - SegmentFault 思否
  8. 你赚不到钱的原因,是你把自己放错了地方
  9. c语言实现一个编译器生成语法树,运用JavaScript构造C语言子集的编译器
  10. 学习测试环境部署,先从学会虚拟机安装系统开始
  11. Windows Server 2012 R2服务器集群测试
  12. GIST特征描述符使用
  13. sample_venc解析
  14. 矩阵转置相关公式_线性代数入门——矩阵的转置运算及对称矩阵的概念
  15. ACER 4741G 笔记本刷1.31版bios黑屏,救砖处理
  16. 笔记本电脑怎么拆开后盖_联想笔记本电脑怎么拆开后盖_联想笔记本怎么拆
  17. Excel的去重、分列
  18. figma安装包_Figma软件下载|UI界面设计软件(Figma)下载 v3.0.4 官方版 - 比克尔下载...
  19. idea 下载vue 插件
  20. Element table 导出Excel重复数据

热门文章

  1. 外部中断器微型计算机课程设计,课程设计-电子时钟参考.doc
  2. UliPad源码+Anaconda+wxPython环境配置
  3. 局域网的主机如何连接外网
  4. java图书管理系统源码免费_Java图书管理系统 附源码
  5. 2021必看!java电子书合集
  6. Linux操作系统安装过程
  7. 一步步教你如何配置Java环境变量(超级详细)
  8. 外汇EA 指标加密方法
  9. 瓜瓜播放器android,瓜瓜视频播放器
  10. 台式计算机 行业标准,GBT 9813.3-2017 计算机通用规范 第3部分:服务器国家标准...