[TOC]
设计模式 - Java篇
文章目录 | |
---|---|
[设计模式简介 \ | 菜鸟教程 (runoob.com)](https://www.runoob.com/design-pattern/design-pattern-intro.html) |
《设计模式(Java版)》—— 韩敬海 | |
/ |
一、简述
设计模式的存在相当于对代码的设计,你写不写的出代码可能跟它关系不大,但是要想写的优美,你就必须要懂,同时它也是地基一样的存在,我尽量以逻辑图形式呈现。
1.1 设计模式的分类
-
创建型
- 用于在创建对象的时候用其他更简洁灵活的方式,而不是直接new一个对象拿出来- 工厂模式(Factory Pattern)
- 单例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
-
结构型
- 类和对象的灵活性,一般是设计类与类之间的继承隔离关系- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 过滤器模式(Filter、Criteria Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
-
行为型
- 对象之间的通信- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 空对象模式(Null Object Pattern)
- 策略模式(Strategy Pattern)
- 模板模式(Template Pattern)
- 访问者模式(Visitor Pattern)
-
J2EE模式
- 表示层,我不太理解,后面看看- MVC 模式(MVC Pattern)
- 业务代表模式(Business Delegate Pattern)
- 组合实体模式(Composite Entity Pattern)
- 数据访问对象模式(Data Access Object Pattern)
- 前端控制器模式(Front Controller Pattern)
- 拦截过滤器模式(Intercepting Filter Pattern)
- 服务定位器模式(Service Locator Pattern)
- 传输对象模式(Transfer Object Pattern)
2.2 设计原则
-
开闭原则
最核心的一句话,
对扩展开放,对修改关闭
,简单来说就是我们的Get/Set方法。 -
里氏代换原则
“任何基类可以出现的地方,子类一定可以出现”,其实就是
多态
。 -
依赖倒转原则
接口编程,核心是
面向接口编程
,减少代码交互的耦合性。 -
接口隔离原则
调用者调这个接口的时候,防止调用到用不上的方法,每个应用都在最小接口上。
-
迪米特法则
实体之间尽量相互独立,不要过于与其他实体之间产生作用,可以利用VO。
-
单一职责原则
一个类应该就干它自己应该干的事情,类似于MVC模式下,持久化层负责数据库操作,控制层负责外部交互。
二、代码级设计模式
2.1 创建型
主要是围绕进行对象的实例化的方式,提供对对象的创建和管理,分为如下几种
- 单例模式
- 工厂模式
- 原型模式
- 建造者模式
单例模式 - 懒汉与饿汉
意思是,就算在多个线程下,这个类也只会有一个实例,一般是会配合static关键字去做,下面聊聊两种代表性的单例模式。
- 饿汉式 - 类加载时就初始化
- 懒汉式 - 要用的时候才初始化
先通过代码去了解一下饿汉式
public class HungryDemo {
/**
* 类加载时就开始实例化
*/
private static final HungryDemo hungryDemo = new HungryDemo();
/**
* 开闭原则获取实例
*
* @return
*/
public static HungryDemo getHungry() {
return hungryDemo;
}
}
如果在懒汉式单例模式下,就会存在,两个线程都是进行获取实例操作,可能会拿到两个实例,这就违背了单例模式的原则,所以接下来看看很厉害的,双重检查锁+懒汉式单例
public class LazyDemo {
private static LazyDemo lazyDemo;
public static synchronized LazyDemo getLazyDemo() {
// 第一关检查:静态锁
if (lazyDemo == null) {
synchronized (LazyDemo.class) {
// 第二关检查:字节码同步锁
if (lazyDemo == null) {
lazyDemo = new LazyDemo();
}
}
}
return lazyDemo;
}
}
单例模式的优点及应用
- 减少内存开支,只有一个实例,可以用在一个对象需要频繁创建销毁的场景
- 避免资源的重复占用
- 全局化资源共享
工厂模式 - 简单/抽象/方法
分为了三种情况
- 简单工厂
- 抽象工厂
- 工厂方法
具体先看看代码,再去理解它的真实意图,首先是简单工厂
工厂方法
工厂方法模式是简单工厂的升级版本,先看看逻辑图
代码就不写了,就是没有抽象类了,变成接口了,跟简单工厂差不多。
抽象工厂
抽象工厂模式又是工厂方法模式的升级版本,主要是为了解决多接口选择的问题,还是画逻辑图。
不写代码了好懒
原型模式
这个模式的好处是大对象的复制会比较快节省本地服务的性能,先上代码,首先是接口
public interface TopInter extends Cloneable {
TopInter clone();
}
其次准备一个继承它的类,重写一下clone方法,其实这个就是深拷贝
public class DemoCre implements TopInter {
@Override
public TopInter clone() {
try {
return (TopInter) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
一般对于大对象我们会这么做
public void testMethod(TopInter topInter){
TopInter topInter1 = topInter.clone();
}
这个模式下构造函数不会执行:因为是从内存中完整复制而不是new,所以不会执行。
建造者模式/生成器模式
当一个类的构造函数参数超过4个,而且参数有些是可选的参数
,考虑使用建造者模式,常规情况下,我们可能会这样涉及
- 写好几行构造方法,利用重载,写一堆没用的代码
- Get/Set传参
其实这么写不仅累,代码还又臭又长,且不美观,所以相应的解决方案也应运而生,大概逻辑如下
- 一个实体类,里面一些参数在new的时候有些必须存在,有些不用存在
- 一个Builder的静态内部类
- 对于必选参数,内部类就做成构造,可选参数,就做成set方法
- 最后,在这个静态内部类里,写一个返回new对象,传入this的实例
文章目录 |
---|
秒懂设计模式之建造者模式(Builder pattern) - 知乎 (zhihu.com) |
建造者模式的使用场景 - yujiwei - 博客园 (cnblogs.com) |
算了,上面的说法我也不太理解,直到我看到了这么一句话:
“我曾经做过一个这样的模块,一个业务系统,数据产生在子系统A里面,同时子系统B需要A所产生的数据,但是需要经过"改造"和"加工",我们打个比方,假如A得到了一个关于交易的情况的一个Json类型的数据包,给B的时候也许有一些属性和字段不需要,或者A没有的而B却需要进行自主的构建,其实假如我们只有这一项的话,完全不需要使用到设计模式,但是假如A和B之间需要交换的数据种类非常的多,并且有可能C系统也会参与进来,甚至D系统也来了。。。这个时候我们不得不想一个统一的办法来管理它们,没错,就是建造者模式。”
我感觉整个人顿时茅塞顿开,下面写个代码示例
undetermined
2.2 结构型
主要围绕的内容是类之间的组装,就像一辆车的选配一样,也分如下
- 代理模式
- 装饰者模式
- 适配器模式
- 组合模式
- 外观模式
- 享元模式
代理模式
通过代理来控制核心对象的访问,非常典型的案例就是JDK动态代理及CgLib动态代理,下面上一个JDK动态代理的案例代码:
接口和实现类
package com.springAOP;
public interface UserDao {
int add(int a, int b);
String update(String id);
void say();}
public class UserDaoImp implements UserDao {
@Override
public int add(int a, int b) {
System.out.println("add 方法正在执行");
return a + b;
}
@Override
public String update(String id) {
System.out.println("update 方法正在执行");
return id;
}
@Override
public void say() {
System.out.println("hello word say");
}}
代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxyRun {
public static void main(String[] args) throws ClassNotFoundException {
// 接口对象数组
Class[] userDaoInter = {UserDao.class};
// new一个实现类对象
UserDaoImp userDao = new UserDaoImp();
// 获得类加载器对象
Class jdk_proxy_run2 = JDKProxyRun.class;
ClassLoader loader2 = jdk_proxy_run2.getClassLoader();
// newProxyInstance 方法的三个参数,代理的类加载器、接口数组对象、接口的增强逻辑实现对象(继承InvocationHandler)
UserDao dao =
(UserDao) Proxy.newProxyInstance (loader2,userDaoInter,new UserDaoProxy (userDao) );
int result = dao.add(2, 3);
dao.update("我要疯了");
dao.say();
System.out.println("result 的返回值:" + result);
}
}
==============================================================================
class UserDaoProxy implements InvocationHandler {
// 1 把代理对象的原对象进行传入
private Object obj;
UserDaoProxy(Object obj) {
this.obj = obj;
}
// 增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 原方法执行前
System.out.println("方法执行前的代码 _ " +
"该方法的名字:" + method.getName()
+ "_传递的参数:" + Arrays.toString(args));
// 增强的逻辑代码
// 第一个是传参过来的方法对象,第二个是传递的参数
Object res = method.invoke(obj, args);
// 方法执行后
System.out.println("方法之后执行的代码:" + obj);
return res; }}
装饰者模式
此前有一位老师告诉我的一句非常形象的话:“就像一个毛胚房,自己DIY。”,在mybatis中也有使用到该设计模式,下面看看示例代码
车的接口
public interface CarBasic {
/**
* 启动点火
*/
public void starter();
}
车的真正实现
public class AudiCar implements CarBasic {
@Override
public void starter() {
System.out.println("启动!点火!");
}
}
装饰器
public class Decorator implements CarBasic {
private CarBasic carBasic;
public Decorator(CarBasic carBasic) {
this.carBasic = carBasic;
}
@Override
public void starter() {
carBasic.starter();
}
}
真正的装饰工具
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(CarBasic carBasic) {
super(carBasic);
}
@Override
public void starter() {
optional();
super.starter();
speed();
}
public void optional() {
System.out.println("选装了霸气的氛围灯");
}
public void speed() {
System.out.println("百秒加速1秒");
}
}
最后测试一下
public class DemoApplication {
public static void main(String[] args) {
// 装饰前
AudiCar audiCar = new AudiCar();
audiCar.starter();
System.out.println("////////////////");
// 装饰后
Decorator decorator = new Decorator(new ConcreteDecorator(audiCar));
decorator.starter();
}
}
你学会了吗
适配器模式
大概场景描述就是,我是苹果手机,我有一条普通的耳机,但是这个耳机线插头不是苹果要用的那款,就需要一个转换头去转换才能插进来,从iPhone7开始都会赠送一个转换头,差不多就是这个意思。
文章目录 |
---|
Java设计模式之《适配器模式》及应用场景 - 唯一浩哥 - 博客园 (cnblogs.com) |
组合模式
一种树状的结构模式,很像一个公司下的组织架构
文章目录 |
---|
简说设计模式——组合模式 - JAdam - 博客园 (cnblogs.com) |
不想写了懒了
桥梁模式
大家解释下来的话语是,”让抽象和实现解耦“,但是对于我而言大概就是下面的这幅图
代码图大概就是这样
你学废了吗
外观模式
与桥梁模式相似,中间新增一个外观角色类,使得子系统与调用方解耦合,下面看看代码
虽然在解耦合这块做的好,但是改代码就变得麻烦了。
享元模式
池技术的重要实现,例如String常量池、数据库连接池、缓冲等,我们写了一个String字符串abc,下一次再写一个,那么这个时候引用的指针会指向同一块内存,不需要再新开辟一个内存空间了,减少了内存开销,享元模式的本质在于内存实例对象的共享
。
相当于是让我做一个池,下面先做一个简单的小模拟
class PersonFactory {
/** 池集合 */
public final static Map<String, Person> poolPersons = new HashMap<>();
public static Person getPerson(String name) {
if (poolPersons.containsKey(name)) {
return poolPersons.get(name);
} else {
Person person = new Person(name, 19);
poolPersons.put(name, person);
return person;
}
}
}
public class ShareModel {
public static void main(String[] args) {
Person hello1 = PersonFactory.getPerson("hello");
System.out.println(hello1);
Person hello2 = PersonFactory.getPerson("hello");
System.out.println(hello2);
}
}
上面的代码会有一个漏洞需要注意,如果此时我将其中一个对象进行修改,那会影响到其他使用这个对象的线程,所以我们需要线程在获取对象后防止对它进行修改。
2.3 行为型
不太好理解,主要关注的是对象与对象之间职责抽象化?对象之间的通信?,分为如下
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 空对象模式
- 策略模式
- 模板方法
- 访问者模式
责任链模式
方法的执行是一种链条式,在SpringMVC中就有非常好的例子,下面写个Demo
@Data
abstract class HandlerDemo {
private HandlerDemo handlerDemo;
/** 业务方法 */
public abstract void talk();
}
class RunHandler extends HandlerDemo {
@Override
public void talk() {
// 自己的业务处理
System.out.println("自己的业务处理" );
// 流转到下面的处理器
if (super.getHandlerDemo() != null) {
System.out.println("流转到下面的处理器___");
super.getHandlerDemo().talk();
} else {
System.out.println("没有处理器了我是末尾");
}
}
}
public class Demo {
public static void main(String[] args) {
HandlerDemo handler1 = new RunHandler();
HandlerDemo handler2 = new RunHandler();
handler2.setHandlerDemo(handler1);
handler2.talk();
}
}
最后的效果就是
自己的业务处理
流转到下面的处理器___
自己的业务处理
没有处理器了我是末尾
命令模式
长得很像代理模式的味道
类膨胀的太多了,最好跟其他设计模式搭配用
解释器模式
用来作语句解释,从网上的案例上看,大多数是用来做数学运算和SQL的底层处理,日常基本用不上
文章目录 |
---|
expression4J 数学公式计算_一路向前-CSDN博客 |
Java表达式引擎fel/groovy/expression4j/java脚本引擎的性能对比【原创】_java_码云网 (findsrc.com) |
迭代器模式
例如Java中的Collection,顺序访问一个容器内的元素,一般我们都是用JDK里面的API,自己写一个新的迭代器应该没有人这么做
文章目录 |
---|
迭代器模式 - 简书 (jianshu.com) |
好懒
中介者模式
主要是解决多个类之间的通信问题,通过在中间增加一个中介者类,将通信的转换处理交给中介者类,降低通信的难度及复杂度
中介者的核心就是将原来相互依赖成网状关系的类分离成星型结构
其实很像MVC模式,以及两个客户端通过Netty服务相互通讯,这个服务端在中间也是变相的一个中介人,不写代码了。
备忘录模式
其实也是一种备份、快照的模式,下面写了一个非常简易版的利用Clone
@Data
@AllArgsConstructor
class Person implements Cloneable {
private String name;
private Integer age;
private Double version;
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.setVersion(person.getVersion() + 1);
return person;
}
}
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Person test01 = new Person("test01", 18, 1.0);
Person clone1 = test01.clone();
clone1.setName("clone1");
Person clone2 = clone1.clone();
clone2.setName("clone2");
System.out.println(test01);
System.out.println(clone1);
System.out.println(clone2);
}
}
当然可以使用集合将每一个版本进入存入,当然后面备份数量膨胀的时候也要考虑到生命周期的管理。
观察者模式
也叫发布/订阅模式,在很多中间件上都有实际案例,例如RabbitMQ、Redis等。
实在是懒得写代码。
状态模式
一个对象在它内部发生改变的时候改变它自身的行为,例如staut=1时切换执行A,staut=2时切换执行B,下面举个例子
@Data
@AllArgsConstructor
class GotoClass {
private String time;
@Override
public String toString() {
switch (this.time) {
case "11.00":
return "语文课";
case "12.00":
return "数学课";
case "13.00":
return "英语课";
default:
return "放学了";
}
}
}
public class Demo {
public static void main(String[] args) {
GotoClass gotoClass = new GotoClass("11.00");
System.out.println(gotoClass);
gotoClass.setTime("12.00");
System.out.println(gotoClass);
gotoClass.setTime("13.00");
System.out.println(gotoClass);
}
}
或者还有另外一种实现方式
@Data
@AllArgsConstructor
class Course {
private String name;
public void getCourse() {
System.out.println("现在上:" + this.name);
}
}
@Data
class GotoClass {
private Course course;
public void say() {
course.getCourse();
}
}
public class Demo {
public static void main(String[] args) {
Course course1 = new Course("语文");
Course course2 = new Course("数学");
Course course3 = new Course("英语");
GotoClass gotoClass = new GotoClass();
gotoClass.setCourse(course1);
gotoClass.say();
gotoClass.setCourse(course2);
gotoClass.say();
gotoClass.setCourse(course3);
gotoClass.say();
}
}
都差不多,上面我没用多态去写,但是实际上我们还是需要用面向对象的编程思维去实现,大概就是这样。
策略模式
与之前的结构型的设计模式还是蛮相似的,比较简单
interface Shape{
public void run();
}
class ShapeImplA implements Shape{
@Override
public void run() {
System.out.println("ShapeImplA");
}
}
class ShapeImplB implements Shape{
@Override
public void run() {
System.out.println("ShapeImplB");
}
}
class Composite{
private Shape shape;
public Composite(Shape shape) {
this.shape = shape;
}
public void execute(){
this.shape.run();
}
}
public class Demo {
public static void main(String[] args) {
Shape shapeA = new ShapeImplA();
new Composite(shapeA).execute();
Shape shapeB = new ShapeImplB();
new Composite(shapeB).execute();
}
}
主要是为了解决这些不同的子类所执行的方法可以独立*切换
模板方法模式
先看看代码的实现
abstract class Entitle {
public abstract void say();
public void templateMethod() {
this.say();
}
}
class EntitleA extends Entitle {
@Override
public void say() {
System.out.println("EntitleA");
}
}
public class Demo {
public static void main(String[] args) {
Entitle entitle = new EntitleA();
entitle.say();
}
}
代码不多只有这么这么几行,变化的地方在子类的方法中,不变的公共的地方放在抽象类,这样在后期的业务扩展上也比较容易修改,符合开闭原则。
访问者模式
1
undetermined
1
2.4 混合设计模式
就是把上面的那些模式组合起来,形成更优雅的设计
- 命令链模式
- 工厂策略模式
- 观察中介者模式
- 规格模式
命令链模式
1
工厂策略模式
1
观察中介者模式
1
规格模式
1
三、应用级设计模式
3.1 J2EE模式
文章目录 |
---|
j2ee_百度百科 (baidu.com) |
是一个分布式应用程序的开发规范,全称:Java 2 Platform Enterprise Edition
MVC 模式
将MVC分开解读
- M - 模型层
- V - 视图层
- C - 控制层
日常的Java开发中经常有涉及到,经典的例子就是SpringMVC,拆分为
- 请求控制层 - Controller
- 业务员接口层 - Service
- 具体业务执行实现 - ServiceImpl
- 数据库操作持久化层 - Mapper
文章目录 |
---|
SpringMVC的基本使用+原理,一篇囊括__-CSDN博客 |
业务代表模式
1
组合实体模式
1
数据访问对象模式
1
前端控制器模式
1
拦截过滤器模式
1
服务定位器模式
1
传输对象模式
1
3.2 内容待定
111111