单一职责原则,LZ当时是这么写的

2019-10-29 作者:联系我们   |   浏览(199)

【设计模式总结】对常用设计模式的一些思考(未完待续。。。),设计模式未完待续

前言

在【Java设计模式】系列中,LZ写了十几篇关于设计模式的文章,大致是关于每种设计模式的作用、写法、优缺点、应用场景。

随着LZ自身的成长,再加上在工作中会从事一定的架构以及底层代码设计的原因,在近半年的实践中,对于设计模式的理解又有了新的认识,因此有了此文,目的是和网友朋友们分享自己对于设计模式的一些思考。LZ本人水平有限,抛砖引玉,写得不对的地方希望网友朋友们指正,也可留言相互讨论。

 

简单工厂模式

首先是简单工厂模式。

对于简单工厂模式的作用描述,LZ当时是这么写的:

原因很简单:解耦。

A对象如果要调用B对象,最简单的做法就是直接new一个B出来。这么做有一个问题,假如C类和B类实现了同一个接口/继承自同一个类,系统需要把B类修改成C类,程序不得不重写A类代码。如果程序中有100个地方new了B对象,那么就要修改100处。

这就是典型的代码耦合度太高导致的"牵一发动全身"。所以,有一个办法就是写一个工厂IFactory,A与IFactory耦合,修改一下,让所有的类都实现C接口并且IFactory生产出C的实例就可以了。

感谢@一线码农的指正,原来我以为这段话是有问题的,现在仔细思考来看这段话没问题。举个最简单的代码例子,定义一个工厂类:

 1 public class ObjectFactory {
 2 
 3     public static Object getObject(int i) {
 4         if (i == 1) {
 5             return new Random();
 6         } else if (i == 2) {
 7             return Runtime.getRuntime();
 8         }
 9         
10         return null;
11     }
12     
13 }

调用方假如不使用工厂模式,那么我定义一段代码:

 1 public class UseObject {
 2 
 3     public void funtionA() {
 4         Object obj = new Random();
 5     }
 6     
 7     public void funtionB() {
 8         Object obj = new Random();
 9     }
10     
11     public void funtionC() {
12         Object obj = new Random();
13     }
14     
15 }

假如现在我不想用Random类了,我想用Runtime类了,此时三个方法都需要把"Object obj = new Random()"改为"Object obj = Runtime.getRuntime();",如果类似的代码有100处、1000处,那么得改100处、1000处,非常麻烦,使用了工厂方法就不一样了,调用方完全可以这么写:

 1 public class UseObject {
 2 
 3     private static Properties properties;
 4     
 5     static {
 6         // 加载配置文件
 7     }
 8     
 9     public void funtionA() {
10         Object obj = ObjectFactory.getObject(Integer.parseInt(properties.getProperty("XXX")));
11     }
12     
13     public void funtionB() {
14         Object obj = ObjectFactory.getObject(Integer.parseInt(properties.getProperty("XXX")));
15     }
16     
17     public void funtionC() {
18         Object obj = ObjectFactory.getObject(Integer.parseInt(properties.getProperty("XXX")));
19     }
20     
21 }

搞一个配置文件,每次调用方从配置文件中读出一个枚举值,然后根据这个枚举值去ObjectFactory里面拿一个Object对象实例出来。这样,未来不管是3处还是100处、1000处,如果要修改,只需要修改一次配置文件即可,不需要所有地方都修改,这就是使用工厂模式带来的好处。

不过简单工厂模式这边自身还有一个小问题,就是如果工厂这边新增加了一种对象,那么工厂类必须同步新增if...else...分支,不过这个问题对于Java语言不难解决,只要定义好包路径,完全可以通过反射的方式获取到新增的对象而不需要修改工厂自身的代码。

上面的讲完,LZ觉得简单工厂模式的主要作用还有两点:

(1)隐藏对象构造细节

(2)分离对象使用方与对象构造方,使得代码职责更明确,使得整体代码结构更优雅

先看一下第一点,举几个例子,比如JDK自带的构造不同的线程池,最终获取到的都是ExecutorService接口实现类:

1 @Test
2 public void testExecutors() {
3     ExecutorService es1 = Executors.newCachedThreadPool();
4     ExecutorService es2 = Executors.newFixedThreadPool(10);
5     ExecutorService es3 = Executors.newSingleThreadExecutor();
6     System.out.println(es1);
7     System.out.println(es2);
8     System.out.println(es3);
9 }

这个方法构造线程池是比较简单的,复杂的比如Spring构造一个Bean对象:

1 @Test
2 public void testSpring() {
3     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
4     Object obj = applicationContext.getBean(Object.class);
5     System.out.println(obj);
6         
7     applicationContext.close();
8     
9 }

中间流程非常长(有兴趣的可以看下我写的Spring源码分析的几篇文章),构造Bean的细节不需要也没有必要暴露给Spring使用者(当然那些想要研究框架源代码以便更好地使用框架的除外),使用者关心的只是调用工厂类的某个方法可以获取到想要的对象即可。

至于前面说的第二点,可以用设计模式六大原则的单一职责原则来理解:

单一职责原则(SRP):
1,SRP(Single Responsibilities Principle)的定义:就一个类而言,应该仅有一个引起它变化的原因。简而言之,就是功能要单一
2,如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏
3,软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离

把这段话加上我的理解就是:该使用的地方只关注使用,该构造对象的地方只关注构造对象,不需要把两段逻辑联系在一起,保持一个类或者一个方法100~200行左右的代码量,能描述清楚要做的一件事情即可

 

单例模式

第二点讲讲单例模式。

拿我比较喜欢的饿汉式单例模式的写法举例吧:

 1 public class Object {
 2 
 3     private static final Object instance = new Object();
 4     
 5     private Object() {
 6         
 7     }
 8     
 9     public static Object getInstance() {
10         return instance;
11     }
12     
13     public void functionA() {
14         
15     }
16     
17     public void functionB() {
18         
19     }
20     
21     public void functionC() {
22         
23     }
24     
25 }

然后我们调用的时候,会使用如下的方式调用functionA()、functionB()、functionC()三个方法:

1 @Test
2 public void testSingleton() {
3     Object.getInstance().functionA();
4     Object.getInstance().functionB();
5     Object.getInstance().functionC();
6 }

这么做是没有问题,使用单例模式可以保证Object类在对象池(也就是堆)中只被创建一次,节省了系统的开销。但是问题是:是否需要使用单例模式,为什么一定要把Object这个对象实例化出来?

意思是Java里面有static关键字,如果将functionA()、functionB()、functionC()都加上static关键字,那么调用方完全可以使用如下方式调用:

1 @Test
2 public void testSingleton() {
3     Object.functionA();
4     Object.functionB();
5     Object.functionC();
6 }

对象都不用实例化出来了,岂不是更加节省空间?

这个问题总结起来就到了使用static关键字调用方法和使用单例模式调用方法的区别上了,关于这两种做法有什么区别,我个人的看法是没什么区别。所谓区别,说到底,也就是两种,哪种消耗内存更少,哪种调用效率更高对吧,逐一看一下:

  • 从内存消耗上来看,真没什么区别,static方法也好,实例方法也好,都是占用一定的内存的,但这些方法都是类初始化的时候被加载,加载完毕被存储在方法区中
  • 从调用效率上来看,也没什么区别,方法最终在解析阶段被翻译为直接引用,存储在方法区中,然后调用方法的时候拿这个直接引用去调用方法(学过C、C++的可能会比较好理解这一点,这叫做函数指针,意思是每个方法在内存中都有一个地址,可以直接通过这个地址拿到方法的起始位置,然后开始调用方法)

所以,无论从内存消耗还是调用效率上,通过static调用方法和通过单例模式调用方法,都没多大区别,所以,我认为这种单例的写法,也是完全可以把所有的方法都直接写成静态的。使用单例模式,无非是更加符合面向对象(OO)的编程原则而已。

写代码这个事情,除了让代码更优雅、更简洁、更可维护、更可复用这些众所周知的之外,不就是图个简单吗,怎么写得简单怎么来,所以用哪种方式调用方法在我个人看来真的是纯粹看个人喜好,说一下我个人的原则:整个类代码比较少的,一两百行乃至更少的,使用static直接调方法,不实例化对象;整个类代码比较多的,逻辑比较复杂的,使用单例模式

毕竟,单例单例,这个对象还是存在的,那必然可以继承。整个类代码比较多的,其中有一个或者多个方法不符合我当前业务逻辑,没法继承,使用静态方法直接调用的话,得把整个类都复制一遍,然后改其中几个方法,相对麻烦;使用单例的话,其中有一个或者多个方法不符合我当前业务逻辑,直接继承一下改这几个方法就可以了。类代码比较少的类,反正复制黏贴改一下也无所谓。

 

模板模式

接着是模板模式,模板模式我本人并没有专门写过文章,因此这里网上找了一篇我认为把模板模式讲清楚的文章。

对于一个架构师、CTO,反正只要涉及到写底层代码的程序员而言,模板模式都是非常重要的。模板模式简单说就是代码设计人员定义好整个代码处理流程,将变化的地方抽象出来,交给子类去实现。根据我自己的经验,模板模式的使用,对于代码设计人员来说有两个难点:

(1)主流程必须定义得足够宽松,保证子类有足够的空间去扩展

(2)主流程必须定义得足够严谨,保证抽离出来的部分都是关键的部分

这两点看似有点矛盾,其实是不矛盾的。第一点是站在扩展性的角度而言,第二点是站在业务角度而言的。假如有这么一段模板代码:

 1 public abstract class Template {
 2 
 3     protected abstract void a();
 4     protected abstract void b();
 5     protected abstract void c();
 6     
 7     public void process(int i, int j) {
 8         if (i == 1 || i == 2 || i == 3) {
 9             a();
10         } else if (i == 4 || i == 4 || i == 5) {
11             if (j > 1) {
12                 b();
13             } else {
14                 a();
15             }
16         } else if (i == 6) {
17             if (j < 10) {
18                 c();
19             } else {
20                 b();
21             }
22         } else {
23             c();
24         }
25     }
26     
27 }

我不知道这段代码例子举得妥当不妥当,但我想说说我想表达的意思:这段模板代码定义得足够严谨,但是缺乏扩展性。因为我认为在抽象方法前后加太多的业务逻辑,比如太多的条件、太多的循环,会很容易将一些需要抽象让子类自己去实现的逻辑放在公共逻辑里面,这样会导致两个问题:

(1)抽象部分细分太厉害,导致扩展性降低,子类只能按照定义死的逻辑去写,比如a()方法中有一些值需要在c()方法中使用就只能通过ThreadLocal或者某些公共类去实现,反而增加了代码的难度

(2)子类发现该抽象的部分被放到公共逻辑里面去了,无法完成代码要求

最后提一点,我认为模板模式对梳理代码思路是非常有用的。因为模板模式的核心是抽象,因此在遇到比较复杂的业务流程的时候,不妨尝试一下使用模板模式,对核心部分进行抽象,以梳理逻辑,也是一种不错的思路,至少我用这种方法写出过一版比较复杂的代码。

 

策略模式

策略模式,一种可以认为和模板模式有一点点像的设计模式,至于策略模式和模板模式之间的区别,后面视篇幅再聊。

策略模式其实比较简单,但是在使用中我有一点点的新认识,举个例子吧:

 1 public void functionA() {
 2     // 一段逻辑,100行
 3 
 4     System.out.println();
 5     System.out.println();
 6     System.out.println();
 7     System.out.println();
 8     System.out.println();
 9     System.out.println();   
10 }

一个很正常的方法funtionA(),里面有段很长(就假设是这里的100行的代码),以后改代码的时候发现这100行代码写得有点问题,这时候怎么办,有两种做法:

(1)直接删除这100行代码。但是直接删除的话,有可能后来写代码的人想查看以前写的代码,怎么办?肯定有人提出用版本管理工具SVN、Git啊,不都可以查看代码历史记录吗?但是,一来这样比较麻烦每次都要查看代码历史记录,二来如果当时的网络不好无法查看代码历史记录呢?

(2)直接注释这100行代码,在下面写新的逻辑。这样的话,可以是可以查看以前的代码了,但是长长的百行注释放在那边,非常影响代码的可读性,非常不推荐

这个时候,就推荐使用策略模式了,这100行逻辑完全可以抽象为一段策略,所有策略的实现放在一个package下,这样既把原有的代码保留了下来,可以在同一个package下方便地查看,又可以根据需求更换策略,非常方便。

应网友朋友要求,补充一下代码,这样的,functionA()可以这么改,首先定义一段抽象策略:

1 package org.xrq.test.design.strategy;
2 
3 public interface Strategy {
4 
5     public void opt();
6     
7 }

然后定义一个策略A:

 1 package org.xrq.test.design.strategy.impl;
 2 
 3 import org.xrq.test.design.strategy.Strategy;
 4 
 5 public class StrategyA implements Strategy {
 6 
 7     @Override
 8     public void opt() {
 9         
10     }
11     
12 }

用的时候这么使用:

 1 public class UseStrategy {
 2 
 3     private Strategy strategy;
 4     
 5     public UseStrategy(Strategy strategy) {
 6         this.strategy= strategy; 
 7     }
 8     
 9     public void function() {
10         strategy.opt();
11         
12         System.out.println();
13         System.out.println();
14         System.out.println();
15         System.out.println();
16         System.out.println();
17         System.out.println();
18     }
19     
20 }

使用UseStrategy类的时候,只要在构造函数中传入new StrategyA()即可。此时,如果要换策略,可以在同一个package下定义一个策略B:

 1 package org.xrq.test.design.strategy.impl;
 2 
 3 import org.xrq.test.design.strategy.Strategy;
 4 
 5 public class StrategyB implements Strategy {
 6 
 7     @Override
 8     public void opt() {
 9         
10     }
11     
12 }

使用使用UseStrategy类的时候,需要更换策略,可在构造函数中传入new StrategyB()。这样一种写法,就达到了我说的目的:

1、代码的实现更加优雅,调用方只需要传入不同的Strategy接口的实现即可

2、原有的代码被保留了下来,因为所有的策略都放在同一个package下,可以方便地查看原来的代码是怎么写的

 

前言 在【Java设计模式】系列中,LZ写了十几篇关于...

具体模板(ConcrteClass)角色:实现父类的一个或多个抽象方法,作为顶层逻辑的组成而存在。

单例模式的双检锁是什么

           

本文会介绍一些经典的设计模式思想:

每个抽象模板可以有多个具体模板与之对应,而每个具体模板有其自己对抽象方法(也就是顶层逻辑的组成部分)的实现,从而使得顶层逻辑的实现各不相同。
四、模板方法模式适用场景
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
3、控制子类扩展。
五、模板方法模式与其它模式 1、策略模式(strategy模式):模板方法使用继承来改变算法的部分,策略模式使用委托来改变整个算法。区别在于封闭的变化不同,一个变化的部分,一个变化的是整体。
2、工厂方法模式(factory method模式):Factory Method模式常被模板方法调用。
六、模板方法模式PHP示例

访问者模式总结

由于访问者模式的使用条件较为苛刻,本身结构也较为复杂,因此在实际应用中使用频率不是特别高。当系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相同时,可以考虑使用访问者模式进行设计。在XML文档解析、编译器的设计、复杂集合对象的处理等领域访问者模式得到了一定的应用。

1.主要优点

      访问者模式的主要优点如下:

(1) 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。

(2) 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。

(3) 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。

2.主要缺点

      访问者模式的主要缺点如下:

(1) 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求。

(2) 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。

3.适用场景

      在以下情况下可以考虑使用访问者模式:

(1) 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。

(2) 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。

(3) 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

            有些对象,我们需要确保在一个程序中有且只能有一个。

常用设计模式

Proxy代理模式

代理模式:为其他对象提供一种代理以便控制对这个对象的访问。

可以详细控制访问某个类(对象)的方法,在调用这个方法前作的前置处理(统一的流程代码放到代理中处理)。调用这个方法后做后置处理。

代理模式分类:

1.静态代理(静态定义代理类,我们自己静态定义的代理类。比如我们自己定义一个明星的经纪人类)

2.动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成)比较重要!!

  • JDK自带的动态代理

  • javaassist字节码操作库实现

  • CGLIB

  • ASM(底层使用指令,可维护性较差)

结构组成

代理模式主要涉及到三个角色:抽象角色、代理角色、真实角色(被代理的角色)。

抽象角色:声明真实对象和代理对象的共同接口。即真实对象和代理对象共同要实现的行为动作(好比房东和中介都要能够实现租房的行为,都能把房子租给你)。

代理角色:代理角色内部含有对真实角色的引用,从而可以去操作真实对象,同时代理对象提供与真实对象的接口,以便在任何时候都能代替真实对象。同时,代理对象在执行真实对象的操作时,也能附加它自己的操作,相当于对真实对象的封装

真实角色:代理角色所代理的对象,亦即我们最终要引用的对象。

Factory工厂模式

工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)

  • 工厂方法模式(Factory Method)

  • 抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

区别:

工厂方法模式:

  • 一个抽象产品类,可以派生出多个具体产品类。

  • 一个抽象工厂类,可以派生出多个具体工厂类。

每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂模式:

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。

  • 一个抽象工厂类,可以派生出多个具体工厂类。

  • 每个具体工厂类可以创建多个具体产品类的实例。

区别:

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

Singleton单例模式

  • 单例模式只能有一个实例。

  • 单例类必须创建自己的唯一实例。

  • 单例类必须向其他对象提供这一实例。

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

图片 1

单例模式结构图

单例模式(Singleton)是几个创建模式中最对立的一个,它的主要特点不是根据用户程序调用生成一个新的实例,而是控制某个类型的实例唯一性,通过上图我们知道它包含的角色只有一个,就是Singleton,它拥有一个私有构造函数,这确保用户无法通过new直接实例它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance()方法负责检验并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反之,如果一个类可以有几个实例共存,就不要使用单例模式。

不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。

不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。

Delegate委派模式

委派模式(Delegate)是面向对象设计模式中常用的一种模式。这种模式的原理为类B和类A是两个互相没有任何关系的类,B具有和A一模一样的方法和属性;并且调用B中的方法,属性就是调用A中同名的方法和属性。B好像就是一个受A授权委托的中介。第三方的代码不需要知道A的存在,也不需要和A发生直接的联系,通过B就可以直接使用A的功能,这样既能够使用到A的各种公能,又能够很好的将A保护起来了。

委派模式的优点:

  • 单一一个类无法表现复杂的设计

  • 设计拆分

  • 方便重用

  • 相对独立

  • 功能定义清晰,行为界面分离

  • 松散耦合,容易扩展

strategy策略模式

定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。(Definea family of algorithms,encapsulate each one, andmake them interchangeable. Strategy lets the algorithmvary independently from clients that use it. )

策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。

图片 2

策略模式结构

当存在以下情况时使用Strategy模式

• 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。

• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。

• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。

• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

Prototype原型模式

原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象不值得,特别是以下的几个场景的时候,可能使用原型模式更简单也效率更高。

• 当一个系统应该独立于它的产品创建、构成和表示时,要使用 Prototype模式

• 当要实例化的类是在运行时刻指定时,例如,通过动态装载;

• 为了避免创建一个与产品类层次平行的工厂类层次时

• 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。(也就是当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适)。

图片 3

原型模式结构

Template模板模式

  • 模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。

  • 板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。

  • 在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。

模板方法应用于下列情况:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

  • 控制子类扩展。模板方法只在特定点调用“ hook”操作 ,这样就只允许在这些点进行扩展。

图片 4

模板模式结构

针对这些设计模式的源码并不是靠几句话能讲清楚的,所以我创建了一个Java架构技术交流群:688583154里面有Java工程化、分布式、高性能、性能调优、Spring,MyBatis,Netty源码设计模式分析等知识点讲解与IT技术、IT职场、在线课程、学习资源分享等,特别注意:我们是免费分享学习资源,阿里架构师分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知。

面向对象:

  • 工作1-5年需要突破技术瓶颈;

  • 对java工作机制,常用设计思想,常用java开发框架掌握熟练的;

 

 

以上就是使用php实现模板方法模式的代码,还有一些关于模板方法模式的概念区分,希望对大家的学习有所帮助。

单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,

任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)

        静态代理:已知代理类的情况下

一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码的运行效率;他们总是在编码的同时,就考虑到了以后的代码维护和升级。甚至,只要分析百分之一的代码后,你就会深刻地体会到,什么样的代码才是一个专业的程序员写的,什么样的代码是一个业余爱好者写的。而这一点是任何没有真正分析过标准代码的人都无法体会到的。

<?php
/**
 * 抽象模板角色
 * 定义抽象方法作为顶层逻辑的组成部分,由子类实现
 * 定义模板方法作为顶层逻辑的架子,调用基本方法组装顶层逻辑
 */
abstract class AbstractClass {

  /**
   * 模板方法 调用基本方法组装顶层逻辑
   */
  public function templateMethod() {
    echo 'templateMethod begin.<br />';
    $this->primitiveOperation1();
    $this->primitiveOperation2();
    echo 'templateMethod end.<br />';
  }

  /**
   * 基本方法1
   */
  abstract protected function primitiveOperation1();

   /**
   * 基本方法2
   */
  abstract protected function primitiveOperation2();
}

/**
 * 具体模板角色
 * 实现父类的抽象方法
 */
class ConcreteClass extends AbstractClass{
  /**
   * 基本方法1
   */
  protected function primitiveOperation1() {
    echo 'primitiveOperation1<br />';
  }

   /**
   * 基本方法2
   */
  protected function primitiveOperation2(){
    echo 'primitiveOperation2<br />';
  }

}

/**
 * 客户端
 */
class Client {

   /**
   * Main program.
   */
  public static function main() {
    $class = new ConcreteClass();
    $class->templateMethod();
  }
}

Client::main();
?>

享元模式

    特点: 透明、重用、低耦合

三、模板方法模式中主要角色
**
抽象模板(AbstractClass)角色:** 定义一个或多个抽象方法让子类实现。这些抽象方法叫做基本操作,它们是顶级逻辑的组成部分。
定义一个模板方法。这个模板方法一般是一个具体方法,它给出顶级逻辑的骨架,而逻辑的组成步骤在对应的抽象操作中,这些操作将会推迟到子类中实现。同时,顶层逻辑也可以调用具体的实现方法

图片 5

            ② 提供静态成员并初始化来保存唯一的实例。

七、模板方法模式 模板方法是一种代码复用的基本技术,模板方法导致一种反射的控制结构,这指的是一个父类调用子类的操作。
其实现过程:准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。
八、重构的原则 重构时应当遵守的原则是:将行为以是移到结构的高端,而将状态尽量移动到结构的低端。
1、应当要所行为而不是状态定义一个类。
2、在实现行为是,是用抽象状态而不是用具体状态。
3、给操作划分层次。
4、将状态的确认推迟到子类中。在父类中,如果需要状态属性的话,可以调用抽象的取值方法,而将抽象的取值方法的实现放到具体子类中。
如果可以遵守以上的而,那么就可以在等级结构中将接口与实现分离,将抽象与具体分离,从而保证代码可以最大限度的被复用。

描述动态代理的几种实现方式,分别说出相应的优缺点

                系统扩展性和维护性增强。

图片 6

适配器模式是什么?什么时候使用

    继承方式(类适配器):① 添加一个元接口,增加功能方法    添加实现类,并重写功能方法

一、意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以在不改变一个算法的结构的情况下重定义该算法的某些特定的步骤【GOF95】
二、模板方法模式结构图

外观模式

                                         ② 添加一个目标接口,增加功能方法    添加实现类,并重写其功能方法

您可能感兴趣的文章:

  • php设计模式 Template (模板模式)
  • PHP设计模式之观察者模式(Observer)详细介绍和代码实例
  • php设计模式 DAO(数据访问对象模式)
  • php设计模式 Proxy (代理模式)
  • php设计模式小结
  • php单态设计模式(单例模式)实例
  • php设计模式之简单工厂模式详解
  • php设计模式 Interpreter(解释器模式)
  • php设计模式 Facade(外观模式)
  • php设计模式 Strategy(策略模式)
  • PHP设计模式之模板方法模式定义与用法详解

适配器模式和装饰器模式有什么区别

    工厂方法把实例化的工作推迟到了子类中去实现。

适配器模式和代理模式之前有什么不同

    单一职责原则:            Single Responsibility Principle                一个类只负责一项职责    SRP

Adapter vs Decorator vs Facade vs Proxy Design Pattern in Java

                在面向对象的基本原则中,也推荐我们多使用合成而不是继承,所以合成方式更适合代理模式

什么是类的单例模式

            使用策略模式可以避免使用多重条件转移的语句

从上面的类图中可以看出,在单例类中有一个构造函数 Singleton ,

但是这个构造函数却是私有的(前面是“ - ”符号),

然后在里面还公开了一个 GetInstance()方法,

通过上面的类图不难看出单例模式的特点,从而也可以给出单例模式的定义

单例模式保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。

    工厂模式的分类:

package singleton;

public class SingletonModel_Hungry {

//private contruct method to prevent from new an object

private SingletonModel_Hungry(){}

private  static SingletonModel_Hungry INSTANCE = new SingletonModel_Hungry();

public static SingletonModel_Hungry getInstance (){

return INSTANCE;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

SingletonModel_Hungry.getInstance();

}

}

package singleton;

public class SingletonModel_Common {

private SingletonModel_Common(){

}

private static SingletonModel_Common instance;

public static synchronized SingletonModel_Common getInstance(){

if(instance == null){

instance = new SingletonModel_Common();

}

return instance;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

SingletonModel_Common bb = SingletonModel_Common.getInstance();

}

}

package singleton;

public class SingletonModel_DoubleCheck {

private SingletonModel_DoubleCheck(){

}

private static volatile SingletonModel_DoubleCheck instance ;

public static SingletonModel_DoubleCheck getInstance (){

if(instance ==null){

synchronized(SingletonModel_DoubleCheck.class){

if(instance==null){

instance =  new SingletonModel_DoubleCheck();

}

}

}

return instance;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

}

}

package singleton;

public class SingletonModel_StaticInnerClass {

private SingletonModel_StaticInnerClass(){

System.out.println("This is constructor.");

}

public static class SingletonModel_StaticInnerHolder{

private static SingletonModel_StaticInnerClass instance = new SingletonModel_StaticInnerClass();

}

public static SingletonModel_StaticInnerClass getInstance (){

return SingletonModel_StaticInnerHolder.instance;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

SingletonModel_StaticInnerClass aa = SingletonModel_StaticInnerClass.getInstance();

}

}

class TestC {

public static void main(String[] args) {

// TODO Auto-generated method stub

SingletonModel_StaticInnerClass aa = SingletonModel_StaticInnerClass.getInstance();

}

}

package singleton;

public enum SingletonModel_Enum {

INSTANCE;

}

                使用聚合方式实现代理,代理类和被代理类的耦合度较低,能方便的替换被代理类的对象。也更容易复用

个人理解:模板方法模式, 顾名思义,就是在这个模式中,有一个提供模板功能的方法,它代表的是一种流程,在业务环境中, 多个子业务有相似的流程但是在细节处理上不同,这样,我们把这个流程抽取出来,做成一个抽象类,再由子类实现不同的方法。

模板方法是基于继承的。 有一个特点是钩子方法,钩子方法的作用主要是返回一个boolean的值,在具体类的流程中,用于判断是否执行完整个流程

    3. 单例模式            Singleton Pattern

单例模式的五种写法

 

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

观察者模式是一种使用频率非常高的设计模式,无论是移动应用、Web应用或者桌面应用,观察者模式几乎无处不在,它为实现对象之间的联动提供了一套完整的解决方案,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。观察者模式广泛应用于各种编程语言的GUI事件处理的实现,在基于事件的XML解析技术(如SAX2)以及Web事件处理中也都使用了观察者模式。

      1.主要优点

      观察者模式的主要优点如下:

(1) 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。

(2) 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。

(3) 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。

(4) 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

      2.主要缺点

      观察者模式的主要缺点如下:

(1) 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。

(2) 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

(3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

      3.适用场景

      在以下情况下可以考虑使用观察者模式:

(1) 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。

(2) 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。

(3) 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

    21. 中介者模式      Mediator Pattern

单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。抽象化是开闭原则的关键。

            因为xml和properties等格式的配置文件是纯文本文件,可以直接通过VI编辑器或记事本进行编辑,且无须编译,因此在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件,而原有的Java代码或C#代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统

里氏代换原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象 。 在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象

           里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

依赖倒转原则(Dependency Inversion  Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

            依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。常用的注入方式有三种,分别是:构造注入,设值注入(Setter注入)和接口注入。构造注入是指通过构造函数来传入具体类的对象,设值注入是指通过Setter方法来传入具体类的对象,而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。

            开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段

接口隔离原则(Interface  Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

         在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。

合成复用原则(Composite Reuse Principle, CRP):尽量使用对象组合,而不是继承来达到复用的目的。

迪米特法则(Law of  Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。

 迪米特法则还有几种定义形式,包括:不要和“陌生人”说话、只与你的直接朋友通信等,在迪米特法则中,对于一个对象,其朋友包括以下几类:

(1) 当前对象本身(this);

(2) 以参数形式传入到当前对象方法中的对象;

(3) 当前对象的成员对象;

(4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;

(5) 当前对象所创建的对象。

    应用场景:

设计原则

    定义一个接口来创建对象,但是让子类来决定哪些类需要实例化。

桥接模式是设计Java虚拟机和实现JDBC等驱动程序的核心模式之一,应用较为广泛。在软件开发中如果一个类或一个系统有多个变化维度时,都可以尝试使用桥接模式对其进行设计。桥接模式为多维度变化的系统提供了一套完整的解决方案,并且降低了系统的复杂度。

1.主要优点

        桥接模式的主要优点如下:

        (1)分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,使它们各自都具有自己的子类,以便任何组合子类,从而获得多维度组合对象。

        (2)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。

        (3)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。

2.主要缺点

        桥接模式的主要缺点如下:

        (1)桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。

        (2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。

3.适用场景

        在以下情况下可以考虑使用桥接模式:

        (1)如果一个系统需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,通过桥接模式可以使它们在抽象层建立一个关联关系。

        (2)“抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

        (3)一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。

        (4)对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

 

除了单例模式,你在生产环境中还用过什么设计模式?

    应用场景:

抽象工厂,模板方法

                handler : 动态代理类的实现类

写出三种单例模式实现

                                                把"被适配器"组一个对象组合到适配器中,以修改目标接口包装被适配器。

下面来看单例模式的结构图(图太简单了)

 

史上最全设计模式导学目录(完整版)

 暂列几种常用的,如需其他详细的请查看: 

访问者模式

        饿汉模式加载类时会比较慢,但运行时获取对象的速度会比较快。

为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。

5.1 模式优点

       外观模式的主要优点如下:

(1) 它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简单,与之关联的对象也很少。

(2) 它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可。

(3) 一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。

5.2 模式缺点

       外观模式的主要缺点如下:

(1) 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活 性。

(2) 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则。

5.3 模式适用场景

       在以下情况下可以考虑使用外观模式:

(1) 当要为访问一系列复杂的子系统提供一个简单入口时可以使用外观模式。

(2) 客户端程序与多个子系统之间存在很大的依赖性。引入外观类可以将子系统与客户端解耦,从而提高子系统的独立性和可移植性。

(3) 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

           Object obj = method.invoke(this.功能对象,args);

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

        

适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。

    24. 备忘录模式      Memento Pattern

虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。

        简单工厂模式

什么是设计模式(Design Patterns)?你用过哪种设计模式?用在什么场合

           

个人理解:享元模式的英文名是flyweight , 就是轻量级的意思。 用到的场合是大量相似的对象(棋盘上的棋子),可以重复利用某些属性。 

这个模式结合了工厂方法模式, 工厂只需要一个, 则使用了单例模式。在工厂中定义一个集合,用于存储固定的对象,key-value的形式,用来判断对象是否存在。静态构造方法, 用于提供工厂实例。用得比较少

       1.主要优点

       享元模式的主要优点如下:

(1) 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。

(2) 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

       2.主要缺点

       享元模式的主要缺点如下:

(1) 享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

(2) 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

    单一职责原则,LZ当时是这么写的。       3.适用场景

       在以下情况下可以考虑使用享元模式:

(1) 一个系统有大量相同或者相似的对象,造成内存的大量耗费。

(2) 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。

(3) 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

        智能代理:提供目标对象额外的一些服务。(火车票代售点,中介)

简单工厂模式、工厂方法模式、抽象工厂模式

                使用继承方式实现代理,代理类和被代理类耦合度较高,不便于扩展,每次增加新的代理都必须重新添加新的代理类。成本较高。

桥接模式

    饿汉模式:是在类被加载时,自动会创建单例实例对象。不管用户是否会去调用该成员。

除了一般的方式,下面的都是安全的。

    两种方式的区别: 组合方式:采用组合方式的适配器结构称为 对象适配器。

本文由美高梅赌堵59599发布于联系我们,转载请注明出处:    单一职责原则,LZ当时是这么写的

关键词: