深入理解模板模式及实际应用

        原来写了一个博客,记一次Spring环境使用模板模式。用了之后美滋滋。但是通过这几天的压力测试。发现,都怪自己太年少,太懵懂,太无知。

现在好好梳理下模板模式的使用姿势。

模板模式基本使用场景:

        模板方法模式是通过父类建立框架,子类在重写了父类部分方法之后,在调用从父类继承的方法,产生不同的效果,通过修改子类,影响父类行为的结果,模板方法在一些开源框架中应用非常多,它提供了一个抽象类,然后开源框架写了一堆子类,如果需要扩展功能,可以继承此抽象类,然后覆写protected基本方法,然后在调用一个类似TemplateMethod()的模板方法,完成扩展开发。

模板模式优点:

        模板方法模式是比较简单的一种设计模式,但是它却是代码复用的一项基本的技术,在类库中尤其重要,它遵循“抽象类应当拥有尽可能多的行为,应当拥有尽可能少的数据”的重构原则。作为模板的方法要定义在父类中,在方法的定义中使用到抽象方法,而只看父类的抽象方法是根本不知道怎样处理的,实际做具体处理的是子类,在子类中实现具体功能,因此不同的子类执行将会得出不同的实现结果,但是处理流程还是按照父类定制的方式。这就是模板方法的要义所在,制定算法骨架,让子类具体实现。 其实也是抽象类的使用优点。

Sping环境使用:

        spring环境中难免的注入属性,然后进行逻辑处理。这时注入就是问题。直接是不能给抽象父类注入的。抽象类不能被实例化。所以就有了子类注入后为父类设置。super.name = this.name;这种反人类的注入方式。最后参考了很多方式优化了仅在抽象父类中使用set方法注入所有公用属性。这里不使用抽象类的构造函数注入也是为了减少子类的重复代码。

JDK 1.8 模板模式和接口默认方法:

        jdk1.8 很大的变化就是函数式编程,和接口默认方法。接口默认方法在《JAVA 8 IN ACTION》 中是对API接口维护者的重大感觉,不用因为接口的改变,而对实现类改变。然后就是默认方法的最接口方法的组合和选择。经过我实际使用发现,在注入属性时还是有很多不便的地方。

这是接口默认方法的方式:https://yq.aliyun.com/articles/18211

我使用了Lambda函数接口实现模板模式更加灵活;

传统模板模式:

 /**
  *	在线银行
  */
abstract class OnlineBanking{
   /**
	* 模板方法
	*/
	public processCustomer(int id){
		Customer c = Datebase.getCustomerWithID(id);
		makeCustomerHappy(c);
	}

   /**
	* 让上帝爽
	*/
	abstract void makeCustomerHappy(Customer c );
}

Lambda版本:

 /**
  *	在线银行
  */
@Service
public class OnlineBanking{
   /**
	* 模板方法
	*/
	public processCustomer(int id,Consumer<Customer> makeCustomerHappy){
		Customer c = Datebase.getCustomerWithID(id);
		makeCustomerHappy.accept(c);
	}
}

// 使用方式
public class Test{
	@Autowried
	OnlineBanking onlineBanking;

	public void test{
	onlineBanking.processCustomer(123,(Consumer c) -> {
		// 做一些羞羞的事
	})
	}
}

至此,完毕。

以下几个知识点是本次使用中反复用到的:

类加载顺序:

一、过程
  Person p = new Person();
  1,JVM会去读取指定路径下的Person.class文件,并加载进内存,
    并会先加载Person的父类(如果有直接父类的情况下)
  2,在堆内存中开辟空间,分配地址。
  3,并在对象空间中,对对象中的属性进行默认初始化
  4,调用对应的构造函数,进行初始化
  5,在构造函数中,第一行会先调用父类中的构造函数进行初始化。
  6,父类初始化完毕后,再对子类的属性,进行显示初始化。
  7,指定构造函数的特定初始化
  8,初始化完毕后,将堆内存中的地址值赋给引用变量。
  即:过程为:
   子类属性默认初始化==》父类构造函数初始化==》子类属性显示初始化!!!
二、特点:
  在子类构造对象时,发现,访问子类构造函数时,父类也运行了。
    一】具体:
      在子类的构造函数中第一行有一个默认的隐式语句。 super();
      但是,当自己在子类构造函数中写了一个super的构造语句,则就不会自动调用了。
    注意:当父类中构造函数为Fu(int x)时,
       子类中则必须用super(6);否则报错!
    子类的实例化过程中:
     子类中所有的构造函数默认都会访问父类中的空参数构造函数。
    三、【重点】为什么子类实例化的时候要访问父类中的构造函数?
     1》子类继承了父类,获取到了父类中的内容(属性),所以在使用父类内容之前
       要先看父类是如何对自己的内容进行初始化的。
        所以子类在构造对象时,就必须访问父类中的构造函数,因此要在子类的构造函数中加入super语句。
     2》如果父类中没有定义空构造函数,那么子类的构造函数必须用super明确调用父类中对应的构造函数
     3》子类构造函数中如果使用this调用了本类构造函数时,则调用this的这个构造函数中super便没有了,
        因为super和this都只能定义在第一行。
        但是可以保证的是,子类中肯定会有其它的构造函数访问父类的构造函数。
     注:super语句必须要定义在子类构造函数的第一行。因为父类的初始化动作要先完成。
     注:java所有的类都是Objiect的子类。

java中 抽象类构造方法的理解

抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已。

在继承了抽象类的子类中通过super(参数列表)调用抽象类中的构造方法

示例代码如下:

运行结果:

抽象类虽然不能自己实例化对象,但是在子类新建对象调用子类的构造方法时会先调用抽象类的无参构造方法,这样一来,就可以给给抽象类的参数赋值了。

猜你喜欢

转载自my.oschina.net/u/3418748/blog/1617291