23种设计模式介绍以及在Java中的实现

版权声明:http://blog.youkuaiyun.com/anxpp https://blog.youkuaiyun.com/anxpp/article/details/51224293

原创不易,转载请注明出处:http://blog.youkuaiyun.com/anxpp/article/details/51224293,谢谢!

    文章比较长,读者可以通过顶端的目录选择要了解的模式,然后通过文章右边的按钮快速返回顶部重新选择一个新的模式浏览

    博主精心准备了大量的示例代码。文章尽量提供与编程相关的例子,而不是像多数其他介绍的文章一样,提供一些感觉挺滑稽的例子(那样的例子可能看完觉得写得很好,然而还是不会用...)。

    本文耗费了作者大量时间,还请亲们给个赞O(∩_∩)O~

    也可以通过CTRL+F并输入要了解的模式并跳到对应位置。

    文章中的示例源码在github上:https://github.com/anxpp/JavaDesignPattern

    文中未给出UML图,如果需要请回复说明,本人也可以画出需要的设计模式对应的UML图。


设计模式介绍及Java描述

概述

    设计模式是针对某一类问题的最优解决方案,是从许多优秀的软件系统中总结出的。

    Java中设计模式(java design patterns)通常有23种。

    模式可以分成3类:创建型、行为型和结构型。

    创建型模式

    创建型模式涉及对象的实例化,特点是不让用户代码依赖于对象的创建或排列方式,避免用户直接使用new创建对象。

    创建型模式有以下5个:

    工厂方法模式抽象工厂方法模式生成器模式原型模式单例模式

    行为型模式

   行为型模式涉及怎样合理的设计对象之间的交互通信,以及怎样合理为对象分配职责,让设计富有弹性,易维护,易复用。

    行为型模式有以下11个:

    责任链模式命令模式解释器模式迭代器模式中介者模式备忘录模式观察者模式状态模式策略模式模板方法模式访问者模式

    结构型模式

    结构型模式涉及如何组合类和对象以形成更大的结构,和类有关的结构型模式涉及如何合理使用继承机制;和对象有关的结构型模式涉及如何合理的使用对象组合机制。

    结构型模式有以下7个:

    适配器模式组合模式代理模式享元模式外观模式桥接模式装饰模式

    模式中涉及的重要角色,会在描述中(加粗字体)介绍出来。下面就逐一介绍。

1、单例模式(Singleton Pattern)

    Ensure a class only has one instance,and provide a global point of access to it.

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    何时使用

  •     当系统需要某个类只有一个实例的时候

    优点

  •     单例模式的类唯一实例由其本身控制,可以很好的控制用户何时访问它。

    单例模式概念很简单,而且也比较常用。

    在使用这个模式的时候,我们要考虑是否会在多线程中使用,如果不会应用于多线程,那写法就足够简单:


 
 
  1. public class SimpleSingleton {
  2. private static SimpleSingleton instance;
  3. private SimpleSingleton(){}
  4. public static SimpleSingleton getIntance(){
  5. if(instance == null)
  6. instance = new SimpleSingleton();
  7. return instance;
  8. }
  9. }

    上例就是一个简单的单例模式实现,使用了懒加载模式。但是多线程中可能会创建多个实例。下面就介绍多线程中的使用。

    如果直接将上面例子应用到多线程中,可以直接把getInstance()设置为同步的(synchronized),但是并不高效,任一之后,只能有一个线程可以调用这个方法,其余的会排队等待。

    所以整个方法做同步不是优解,那就只同步代码块就好了。这就引出了双重检验锁,即在同步块外检查一次null,然后再在同步块内检查一次。但是最终这种方式也是会有问题的,使用静态内部类是一种比较好的方式。

    单例模式使用很频繁,也很简单,但不一定都能写对,详细的写法请参考:如何正确地写出单例模式。里面详细的分析的单例模式的各种写法。

    其他模式中的示例代码,有很多时候用到了单例模式,此处就不额外添加例子了。

    这里给出一个推荐的实现方式(枚举):


 
 
  1. public enum EasySingleton{
  2. INSTANCE;
  3. }

2、工厂方法模式(Factory Method Pattern)

    别名:虚拟构造(Another Name:Virtual Constructor)。

    Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclassess.

    定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

    何时使用

  •     用户需要一个类的子类的实例,但不希望与该类的子类形成耦合
  •     用户需要一个类的子类的实例,但用户不知道该类有哪些子类可用

    优点

  •     使用工厂方法可以让用户的代码和某个特定类的子类的代码解耦
  •     工厂方法使用户不必知道它所使用的对象是怎样被创建的,只需知道该对象有哪些方法即可。

    简单工厂模式

    介绍工厂方法模式前,先介绍一下简单工厂模式,简单工厂模式也是一种工厂方法模式。

    简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。

    如果一个一些对象(产品),已经确定了并不易改变和添加新的产品,那么久可以使用简单工厂模式。下面就是简单工厂的例子:


 
 
  1. //演示简单工厂
  2. public class SimpleFactory {
  3. public static void main(String args[]) throws Exception{
  4. Factory factory = new Factory();
  5. factory.produce("PRO5").run();
  6. factory.produce("PRO6").run();
  7. }
  8. }
  9. //抽象产品
  10. interface MeizuPhone{
  11. void run();
  12. }
  13. //具体产品X2
  14. class PRO5 implements MeizuPhone{
  15. @Override
  16. public void run() {
  17. System.out.println("我是一台PRO5");
  18. }
  19. }
  20. class PRO6 implements MeizuPhone{
  21. @Override
  22. public void run() {
  23. System.out.println("我是一台PRO6");
  24. }
  25. }
  26. //工厂
  27. class Factory{
  28. MeizuPhone produce(String product) throws Exception{
  29. if(product.equals("PRO5"))
  30. return new PRO5();
  31. else if(product.equals("PRO6"))
  32. return new PRO6();
  33. throw new Exception("No Such Class");
  34. }
  35. }

    很容易看出,简单工厂模式是不易维护的,如果需要添加新的产品,则整个系统都需要修改。如果我们需要添加诸如PRO7、PRO8等产品,直接在工程类中添加即可。但是如果这时候根部不知道还有什么产品,只有到子类实现时才知道,这时候就需要工厂方法模式。

    而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以实现起来是比较麻烦的,那么工厂方法模式正式解决这个问题的,下面就介绍工厂方法模式。

    工厂方法模式

    工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。

    针对上面的例子,如果使用工厂方法模式,即将工厂定义为一个接口,然后由具体的工厂来确定需要生成什么样的产品,为了与简单工厂比较,这里还是贴上代码:


 
 
  1. //工厂方法模式
  2. public class FactoryMethod {
  3. public static void main(String args[]){
  4. IFactory bigfactory;
  5. bigfactory = new SmallFactory();
  6. bigfactory.produce().run();
  7. bigfactory = new BigFactory();
  8. bigfactory.produce().run();
  9. }
  10. }
  11. //抽象产品
  12. interface MeizuPhone{
  13. void run();
  14. }
  15. //具体产品*2
  16. class PRO5 implements MeizuPhone{
  17. @Override
  18. public void run() {
  19. System.out.println("我是一台PRO5");
  20. }
  21. }
  22. class MX5 implements MeizuPhone{
  23. @Override
  24. public void run() {
  25. System.out.println("我是一台MX5");
  26. }
  27. }
  28. interface IFactory{//抽象的工厂
  29. MeizuPhone produce();
  30. }
  31. //工厂*2
  32. class BigFactory implements IFactory{
  33. @Override
  34. public MeizuPhone produce() {
  35. return new PRO5();
  36. }
  37. }
  38. class SmallFactory implements IFactory{
  39. @Override
  40. public MeizuPhone produce() {
  41. return new MX5();
  42. }
  43. }

    如果了解Java的集合框架,那么它就是一个很好的例子:

    Java中的Collection接口的实现都能通过iterator()方法返回一个迭代器,而不同的实现的迭代器都在该实现中以内部类的方式对Iterator接口实现的,然后通过iterator()方法返回。那么,这个iterator()方法就是一种工厂方法。

    可以看到,在这里抽象产品是Iterator接口,具体产品就是Collection接口的实现中对Iterator接口的实现,构造者是Collection接口,其提供的工厂方法就是Iterator iterator();,具体构造者就是Collection的实现。而工厂方法模式的结构,也就是由前面加粗的4部分组成。

    如果对Java容器不熟悉,下面再提供一个例子(模仿Iterator,其实顺便也介绍了Iterator):

    如果有多种数据结构要遍历,我们就需要一种用于遍历不同结构的工具,首先我们就需要为这个工具定义一个接口(抽象产品),用于描述如何来遍历:


 
 
  1. //只是需要遍历一堆数据,那么只需要2个方法就可以了
  2. public interface Iterator<T> {
  3. boolean hasNext(); //是否还有下一个元素
  4. T next(); //得到下一个元素
  5. }

    然后就是我们要遍历的目标,而这些目标此处我们暂定为列表,这就是构造者


 
 
  1. //便于介绍,不做多的操作
  2. public interface List<T> {
  3. Iterator<T> iterator(); //返回一个遍历器
  4. boolean add(T t); //添加元素到列表
  5. }

    对于List可能有多种实现方式,比如数组和链表,此处就简陋的介绍一下,而这些就是具体构造者,而里面有遍历器的具体实现(具体产品),此处以内部类的形式放到了List的实现(具体构造者)里面,也完全可以修改代码将遍历器的实现(具体产品)独立出来:

    数组的实现:


 
 
  1. package com.anxpp.designpattern.factorymethod;
  2. //方便演示而实现的简陋的数组list
  3. public class ArrayList<T> implements List<T>{
  4. private int size; //存放的元素个数,会默认初始化为0
  5. private Object[] defaultList; //使用数组存放元素
  6. private static final int defaultLength = 10;//默认长度
  7. public ArrayList(){ //默认构造函数
  8. defaultList = new Object[defaultLength];
  9. }
  10. @Override
  11. public Iterator<T> iterator() {
  12. return new MyIterator();
  13. }
  14. //添加元素
  15. @Override
  16. public boolean add(T t) {
  17. if(size<=defaultLength){
  18. defaultList[size++] = t;
  19. return true;
  20. }
  21. return false;
  22. }
  23. //遍历器(具体产品)
  24. private class MyIterator implements Iterator<T>{
  25. private int next;
  26. @Override
  27. public boolean hasNext() {
  28. return next<size;
  29. }
  30. @SuppressWarnings("unchecked")
  31. @Override
  32. public T next() {
  33. return (T) defaultList[next++];
  34. }
  35. }
  36. }

    链表实现:


 
 
  1. //方便演示而实现的简陋的单向链表list
  2. public class LinkList<T> implements List<T>{
  3. private int size; //存放的元素个数,会默认初始化为0
  4. private Node<T> first; //首节点,默认初始化为null
  5. @Override
  6. public Iterator<T> iterator() {
  7. return new MyIterator();
  8. }
  9. @Override
  10. public boolean add(T t) {
  11. if(size==0){
  12. first = new Node<T>(t,null);
  13. size++;
  14. return true;
  15. }
  16. Node<T> node = first;
  17. while(node.next!=null)
  18. node = node.next;
  19. node.next = new Node<T>(t,null);
  20. size++;
  21. return true;
  22. }
  23. //链表节点
  24. private static class Node<T>{
  25. T data;
  26. Node<T> next;
  27. Node(T data,Node<T> next){
  28. this.data = data;
  29. this.next = next;
  30. }
  31. }
  32. //遍历器
  33. private class MyIterator implements Iterator<T>{
  34. private Node<T> next; //下一个节点
  35. MyIterator(){
  36. next = first;
  37. }
  38. @Override
  39. public boolean hasNext() {
  40. return next != null;
  41. }
  42. @Override
  43. public T next() {
  44. T data = next.data;
  45. next = next.next;
  46. return data;
  47. }
  48. }
  49. }

    使用上述代码(模式的使用):


 
 
  1. package com.anxpp.designpattern.factorymethod;
  2. public class TestUse {
  3. public static void main(String args[]){
  4. //分别定义两种结构
  5. List<Integer> array = new ArrayList<Integer>();
  6. List<Integer> link = new LinkList<Integer>();
  7. //添加数据
  8. for(int i = 1;i < 8; i++){
  9. array.add(i);
  10. link.add(i);
  11. }
  12. //获得迭代器
  13. Iterator<Integer> ai = array.iterator();
  14. Iterator<Integer> li = link.iterator();
  15. //遍历并输出
  16. while(ai.hasNext())
  17. System.out.print(ai.next());
  18. System.out.println();
  19. while(li.hasNext())
  20. System.out.print(li.next());
  21. }
  22. }

    控制台会输出:

 
 
  1. 1234567
  2. 1234567

    这就是工厂方法模式,其中遍历器也算是一种迭代器设计模式,后面会介绍。我不会跟你讲什么造车,造轮船,造人,我会给出实际应用。这里只是其中一种应用的举例,当一个接口的一系列实现需要另外的对象对其进行相同操作时,我们就可以这样用:在这个接口中定义返回另外一个对象的方法(工厂方法),然后再在这个接口的实现中,返回对其操作的对象。

    上面这个例子会在迭代器模式中给出完整的实现代码。

    一抽象产品类派生出多个具体产品类;一抽象工厂类派生出多个具体工厂类;每个具体工厂类只能创建一个具体产品类的实例。 即定义一个创建对象的接口(即抽象工厂类),让其子类(具体工厂类)决定实例化哪一个类(具体产品类)。“一对一”的关系。

    与简单工厂间的取舍:工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。 反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。

    可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。

    如果再分得详细一点,一个工厂可能不只是生产手机(如小米除了手机,连电饭锅都有),但有得工厂智能生成低端的产品,而大一点的工厂可能通常是生成更高端的产品。所以一个工厂是不够用了,这时,就应该使用抽象工厂来解决这个问题。

 

3、抽象工厂方法模式(Abstract Factory Pattern)

    别名:配套(Another Name:Kit)

    Provide an interface for creating families of related or dependent objects without specifying their concrete classess.

    提供一个创建一系列或相互依赖对象的接口,而无须指定他们的具体的类。

    何时使用:

    优点: 

    上述生成魅族产品的例子中,我们只生产了手机,但是它不止有手机一种产品,可能还有其他的,比如耳机,为了还可以生成耳机,我们需要对上例进行扩展。

    我们先给出上面生成手机的例子的扩展后的抽象工厂模式代码,以比较这几种模式:


 
 
  1. //抽象工厂模式
  2. public class AbstractFactory {
  3. public static void main(String args[]){
  4. IFactory bigfactory = new BigFactory();
  5. IFactory smallfactory = new BigFactory();
  6. bigfactory.producePhone().run();
  7. bigfactory.produceHeadset().play();
  8. smallfactory.producePhone().run();
  9. smallfactory.produceHeadset().play();
  10. }
  11. }
  12. //抽象产品*2
  13. interface Headset{
  14. void play();
  15. }
  16. //抽象产品
  17. interface MeizuPhone{
  18. void run();
  19. }
  20. //具体产品*2*2
  21. class PRO5 implements MeizuPhone{
  22. @Override
  23. public void run() {
  24. System.out.println("我是一台PRO5");
  25. }
  26. }
  27. class MX5 implements MeizuPhone{
  28. @Override
  29. public void run() {
  30. System.out.println("我是一台MX5");
  31. }
  32. }
  33. class EP21 implements Headset{
  34. @Override
  35. public void play() {
  36. System.out.println("我是一副EP21");
  37. }
  38. }
  39. class EP30 implements Headset{
  40. @Override
  41. public void play() {
  42. System.out.println("我是一台EP30");
  43. }
  44. }
  45. //抽象工厂
  46. interface IFactory{
  47. MeizuPhone producePhone();
  48. Headset produceHeadset();
  49. }
  50. //具体工厂*2
  51. class BigFactory implements IFactory{
  52. @Override
  53. public MeizuPhone producePhone() {
  54. return new PRO5();
  55. }
  56. @Override
  57. public Headset produceHeadset() {
  58. return new EP30();
  59. }
  60. }
  61. //具体工厂*2
  62. class SmallFactory implements IFactory{
  63. @Override
  64. public MeizuPhone producePhone() {
  65. return new MX5();
  66. }
  67. @Override
  68. public Headset produceHeadset() {
  69. return new EP21();
  70. }
  71. }

    在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式(不如上例减去耳机这种产品,就回到工厂方法模式了)。

    这样举例子其实很空洞,这里只是为了比较三种模式,给出抽象的例子才更容易看出区别。

    那么上例中实际应用就是生产迭代器的例子,这里也对齐扩展来介绍抽象工厂模式。Iterator迭代器是Collection专属的,但是现在我们希望能生产Map的迭代器,我们都知道,Map不是继承自Collection的,遍历的方式是不一样的,这就相当于2个产品族,接下来我们就要来实现它。

    为了演示我们如果实现这个同时能生产Map和Collection的迭代器,我会将例子一步步贴出来:

    首先是抽象产品,用来描述迭代器的公共接口:


 
 
  1. //抽象产品
  2. public interface IIterator<T> {
  3. boolean hasNext();
  4. Object next();
  5. }

    然后是抽象工厂,用来返回不同迭代器:


 
 
  1. //抽象工厂
  2. public interface IIteratorFactory<T> {
  3. IIterator<T> iteratorMap(Map<T,Object> m);
  4. IIterator<T> iteratorCollection(Collection<T> c);
  5. }

    接下来是具体产品。

    Collection的迭代器(具体产品):


 
 
  1. //具体产品,Collection迭代器(用到了代理模式)
  2. public class IteratorCollection<T> implements IIterator<T>{
  3. Iterator<T> iterator;
  4. public IteratorCollection(Collection<T> c){
  5. iterator = c.iterator();
  6. }
  7. @Override
  8. public boolean hasNext() {
  9. return iterator.hasNext();
  10. }
  11. @Override
  12. public T next() {
  13. return iterator.next();
  14. }
  15. }

    Map的迭代器(具体产品):


 
 
  1. //具体产品,Map迭代器(用到了代理模式)
  2. public class IteratorMap<T> implements IIterator<T>{
  3. Iterator<Map.Entry<T, Object>> iterator;
  4. public IteratorMap(Map<T,Object> m){
  5. iterator = m.entrySet().iterator();
  6. }
  7. @Override
  8. public boolean hasNext() {
  9. return iterator.hasNext();
  10. }
  11. @Override
  12. public Object next() {
  13. return iterator.next().getValue();
  14. }
  15. }

    完成具体产品设计后,我们就要实现具体的工厂了:


 
 
  1. //具体工厂
  2. public class IteratorFactory<T> implements IIteratorFactory<T>{
  3. @Override
  4. public IteratorMap<T> iteratorMap(Map<T,Object> m) {
  5. return new IteratorMap<T>(m);
  6. }
  7. @Override
  8. public IteratorCollection<T> iteratorCollection(Collection<T> c) {
  9. return new IteratorCollection<T>(c);
  10. }
  11. }

    至此,这个小框架就完成了,我们可以使用它来遍历Collection(List,Set,Queue都是集成自它)和Map: 


 
 
  1. //测试使用
  2. public class TestUse {
  3. public static void main(String args[]){
  4. IIteratorFactory<Integer> factory = new IteratorFactory<>();
  5. Collection<Integer> collection
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值