读书笔记:“幸福并非瞬间发生,他与运气、概率无关,用钱买不到,也不能倚仗权势巧取豪夺。它不受外在事物操纵,而取决于我们对外在事物的阐释”。这句话触动很大,很多时候的精神内耗,无妄的想象揣测导致我们对外在的看法变得消极。阿德勒心理学提出的目的论,引导我们的就是改变自己对外在事物的阐释,从而做到他者信赖、他者贡献,主动去改变自己,也可以改变世界,建立维护双向的良好关系。
一、前言背景
二、模板方法模式
2.1 实战demo-场景
2.2 模板方法模式的优点
2.3 tomcat 源码应用模板模式
三、外观模式(门面模式)
3.1 实战demo-场景
3.2 外观模式的优点
3.3 tomcat 源码应用外观模式
四、单例模式
4.1 实战demo-场景
4.1.1 线程不安全-懒汉单例
4.1.2 线程安全-懒汉-单例模式
4.1.3 线程安全-饿汉-单例模式
4.2 单例模式优点
4.3 Spring 源码应用单例模式
在上一篇系列文章《设计模式觉醒系列(01)设计模式的基石 | 六大原则的核心是什么?》,我们已经详细分享了设计模式的六大原则,以及总结设计模式在研发设计过程中的核心作用。可以说,设计模式就是专门帮我们解决设计问题的经典方案,也是帮助研发人员提高代码可复用性、可维护性、可扩展性、可阅读性的内功心法。
设计模式总共有20多种,如果一篇一篇来写,可以写很长时间,读者也可以订阅关注很长时间。实际而言,很多设计模式都是基于六大基本原则衍生出来,都有共性,为了减少阅读疲劳,以及帮助有缘同学尽快掌握了解齐全这20多种设计模式,后续每篇文章,我们尽量至少分享2个设计模式,期望10篇左右分享完毕。今天我们一次分享三个最简单实用、又非常接地气的设计模式,相信你一定都见过、用过。
附带多说一句,每个设计模式并非完美,都有优缺点。没有绝对的完美解决方案,唯有合适的业务场景应用合适的设计模式,争取最佳实践效果。
模板方法模式(Template Method Pattern),英文原义是:定义一个算法的框架,将一些步骤的实现,延迟到子类中去。在模板方法模式里,父类定义了一个模板方法(它真的就是一个方法method()),然后在这个方法里包含了一系列的操作,比如按顺序调用方法a、b、c、d。而其中的某些操作父类并没有实现,并定义为抽象方法,让子类去实现。
比如以下非常简单的demo,定一个美好一天的抽象类,里面有个模板方法start(),模板方法里按顺序调用了起床、刷牙、吃早餐的方法。
其他三个方法,父类已经实现起床方法getup()+一个吃早餐方法eatBreakfast(),而刷牙方法延迟给子类去实现。
// 抽象美好一天类
abstract class AbstractNiceDay {
// 模板[方法],定义一天的开始,需要起床,刷牙,吃早餐
public final void start() {
getup();
brushtooth();
eatBreakfast();
}
// 起床,公共方法,父类已经实现好
private void getup() {
System.out.println("拉丁解牛说技术,每个人早上都会起床");
}
// 刷牙,抽象方法,由子类实现,有一些人可能不爱刷牙,比如小朋友
protected abstract void brushtooth();
// 吃早餐,公共方法,父类已经实现好
private void eatBreakfast() {
System.out.println("拉丁解牛说技术,每个人早上都吃早餐");
}
具体应用,如果小火、小美他们都开启美好的一天,就分别继承这个相信这个AbstractNiceDay父类,实现里面刷牙方法就可以复用父类的模板方法start()正式开启快乐的一天。这个很直观,大家都能理解,也就不用画图说明。
假如我们需要设计开发一个文档生成器,文档生成包括新建具体文档、文档增加水印标签、保存文档三个步骤。
package lading.java.designpattern.templatemethod;
/**
* 业务场景:文档生成器,支持PDF,word,excel,csv,ppt等文件
* 文档处理器-抽象类
* 包含三个步骤,
* 1、生成具体文档
* 2、文档加水印
* 3、保存文档
*/
public abstract class AbstractDocumentGenerator {
//定义模板方法,按顺序生成文档
public final void generateDocument() {
createDocument();
addTarget();
saveDocument();
}
//子类自己实现
protected abstract void createDocument();
//文档加水印,已实现
protected void addTarget() {
System.out.println("给文档内容加拉丁解牛说技术的水印或者标识");
}
protected void saveDocument() {
System.out.println("保存文档到固定文档目录/file/ladingjieniu/doc");
}
}
package lading.java.designpattern.templatemethod;
/**
* 业务场景:文档生成器,支持PDF,word,excel,csv,ppt等文件
* 文档处理器-抽象类
* 包含三个步骤,
* 1、生成具体文档
* 2、文档加水印
* 3、保存文档
*/
public abstract class AbstractDocumentGenerator {
//定义模板方法,按顺序生成文档
public final void generateDocument() {
createDocument();
addTarget();
saveDocument();
}
//子类自己实现
protected abstract void createDocument();
//文档加水印,已实现
protected void addTarget() {
System.out.println("给文档内容加拉丁解牛说技术的水印或者标识");
}
protected void saveDocument() {
System.out.println("保存文档到固定文档目录/file/ladingjieniu/doc");
}
}
子类可以在严格遵守父类(或者说算法)模板结构的前提下,重新自定义实现具体的某些步骤。这里充分体现了六大核心原则的开闭原则(OCP),对具体步骤实现修改开放,对模板规则框架修改封闭。
此外也提高代码的复用性,子类复用父类已实现的部分步骤方法,减少代码编写。
以及也提高代码可扩展性。
在tomcat源码里,javax.servlet.http.HttpServlet 类的service方法,也是应用了模板方法。service()方法就是个模板方法,他会根据不同请求类型调用相应的处理方法。而里面调用的具体方法,比如doGet、doPost,这些方法在子类可以自定义去实现。源码:
外观模式(facade),也叫门面模式。它的定义是:一个子系统的外部与其内部的通信必须通过一个统一的对象进行。具体是:外观模式为子系统的一组接口提供一个高层次的接口(IFacade,或者定一个xxFacade外观类)。这样可以隐藏子系统的复杂性,使得子系统更易于使用。当客户端调用子系统时候,只需要通过这个高层次的外观接口(Facade类)就可以调用子系统的功能,从而达到客户端与子系统解耦。
比如说一会3.1例子demo的订单系统,订单系统有支付、库存、物流分配三个子系统功能,外部第三方平台需要调用我们订单系统下定单。我们可以定义一个专门处理订单的OrderFacade类,封装好三个功能,供外部客户端去调用。
具体如下,通过OrderFacade类封装好底层内部的支付、物流、库存管理功能。外部client只需要调用OrderFacade类就可以实现下单功能。
package lading.java.designpattern.facade;
/**
* 物流分配子模块
*/
public class LogisticsService {
public void assignExpress(){
System.out.println("说技术拉丁分配物流公司接单安排派送");
}
}
package lading.java.designpattern.facade;
/**
* 支付服务模块
*/
public class PayService {
public void processPay(){
System.out.println("拉丁处理订单支付扣款事宜");
}
}
package lading.java.designpattern.facade;
/**
* 库存管理模块
*/
public class StoreService {
public void deductProd(){
System.out.println("ladingjieniu 减少产品库存");
}
}
package lading.java.designpattern.facade;
/**
* 内部订单系统门面-外观类
*/
public class OrderFacade {
//内置相关子类功能
private LogisticsService logisticsService = new LogisticsService();
private PayService payService = new PayService();
private StoreService storeService = new StoreService();
public void processOrder() {
payService.processPay();
storeService.deductProd();
logisticsService.assignExpress();
}
/**
* 客户端调用下单
*
* @param args
*/
public static void main(String[] args) {
OrderFacade facade = new OrderFacade();
facade.processOrder();
}
}
外观模式非常像公司内部的门户,通过提供高层次的外观模式类,隐藏子系统的交互细节,有效降低客户端与内部模块的耦合度。
由于解耦,子系统的修改,不影响外观类,也不影响客户端的调用,提高代码的可扩展性、可维护性。
外观模式充分体现了依赖倒转原则(细节依赖抽象,降低模块依赖)和迪米特法则(最少知道,一个类对另一个类知道越少越好)的思想。
在tomcat源码里,RequestFacade就是外观模式的经典标准应用。RequestFacade实现了HttpServletRequest接口,底层封装了Request对象,客户端ApplicationDispatcher在应用Request时候,就不需要关系Request的底层逻辑,通过RequestFacade类就可以调用,实现解耦。
此外tomcat里的ResponseFacade、StandardSessionFacade都是用了外观模式。
单例模式,singleton pattern,原义很简单:确保一个类只有一个实例,并自行实例化给整个系统使用。这个特点决定里单例模式适合在对资源开销敏感、要求资源共享复用场景下应用,比如缓存、静态资源。
单例模式有几种实现方式,一个是懒汉模式,等需要用到实例的时候再去实例化,这种模式需要注意线程安全。另一个是饿汉模式,在类加载的时候就实例化对象,这种是线程安全的。接下来对这两种模式的三个案例提供demo分享。
package lading.java.designpattern.singleton;
/**
* 并发不安全,单例模式
*/
public class UnsafeSingleton {
private static UnsafeSingleton singleton;
//避免外部构造多个对象
private UnsafeSingleton() {
}
//仅该方法可以获得实例
public static UnsafeSingleton getSingleton() {
//多个线程同时执行到这部分,就会出现多个实例
if (singleton == null) {
singleton = new UnsafeSingleton();
}
return singleton;
}
}
package lading.java.designpattern.singleton;
/**
* 懒汉单例,双重检查,线程安全的单例模式
*/
public class SafeSingletonLazy {
private static volatile SafeSingletonLazy singleton;
//私有,防止外部实例化
private SafeSingletonLazy() {
}
public static SafeSingletonLazy getSingleton() {
if (singleton == null) {
synchronized (SafeSingletonLazy.class) {
if (singleton == null) {
singleton = new SafeSingletonLazy();
}
}
}
return singleton;
}
}
package lading.java.designpattern.singleton;
/**
* 饿汉-单例模式,线程安全
* 单例对象在加载的时候就实例化好该实例,线程安全
*/
public class SafeSingletonHungry {
private static final SafeSingletonHungry singletonHungry = new SafeSingletonHungry();
//私有化,防止外部实例化
private SafeSingletonHungry() {
}
public static SafeSingletonHungry getInstance() {
return singletonHungry;
}
}
通过确保一个类在整个应用程序生命周期里只有一个实例对象,直接好处就是减少创建对象的开销,为系统节省资源。
我们熟悉的spring的IOC容器的实例管理有经典实践,@Bean默认就是单例模式,也可以通过@Scope("prototype")进行修改。
推荐阅读拉丁解牛相关专题系列(欢迎交流讨论公众号搜:拉丁解牛说技术):
1、JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
2、JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
4、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?
6、JAVA并发编程系列(13)Future、FutureTask异步小王子
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。