标签:之禅 -- void Component 接口 class 设计模式 public
title: 设计模式之禅
date: 2018/8/23 21:12:55
tags: [设计模式]
categories:
- 开发
- java
设计模式学习笔记
-
《设计模式之禅》
-
《大话设计模式》
设计原则(SOLID)
1.单一职责
- There should never be more than one reason for a class to change.
- 现实中做到类的单一职责是很难的,所以我们就用接口来做约束
- 最佳实践 : 接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化
2. 里氏替换原则
- 只要父类能出现的地方,子类就可以正确地替换它,对使用者是透明的 ,但是反过来就不行了
- 子类出现的地方,父类未必能替换,因为子类继承自父类 又异于父类
- overwrite: 子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更加宽松
- 最佳实践:尽量避免子类的“个性” -- 属性和行为,因为把子类当作父类用时,子类的个性会被抹杀
3. 依赖倒置
-
依赖形式
-
构造注入
public Driver(Icar car);
-
setter
class Driver{ ICar car ; public setCar(ICar car){ this.car = car; } ... }
-
接口依赖(在接口方法中定义另外的接口参数)
interface IDriver{ // method drive(ICar car); }
-
-
现实生活中我们处理着很多依赖细节(实现)的事物,
程序世界对现实世界的事物进行了抽象,抽象的结果就是有了抽象类和接口,
然后我们设计系统就产生了抽象间的依赖,”倒置“ 就是从这里产生的
-
最佳实践
- 我们应该依赖接口或者说依赖抽象
- 变量的表面类型尽量是接口或者是抽象类
4. 接口隔离
-
与单一职责原则的区别:
单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑的划分,而接口隔离原则要求接口的方法尽量少。一个职责可能包含10个方法,这10个方法都放到一个接口中,但不同模块分别只访问其中几个,在系统外通过文档约束"不使用的方法",这是单一职责原则允许的,但按照接口隔离原则是不允许的。
-
接口要尽量小(纯洁性)。根据接口隔离原则拆分接口时,首先必须满足单一职责原则。 虽然说要尽量小 ,但是不能拆得稀巴烂
-
最佳实践: **一个接口只服务于一个子模块或业务逻辑 **
5. 迪米特原则
- 也称之为最少知识原则 (关于我 你知道的越少越好)
- 也可解释为对外耦合类知道的越少越好 (我知道怎么简单调用就行,不想知道你内部是怎么复杂实现的)
- 最佳实践
- 要求类羞涩一点,不要对外提供太多的public方法,属性
6. 开闭原则
- 对修改关闭,对扩展开放,java世界里最基础的设计原则
- 前面5个原则都是开闭原则的具体形态,它本身才是精神领袖
设计模式
单例模式
使用场景
- 系统只需要一个实例对象,比如唯一的序列号生成器
- 或者需要考虑资源消耗而只允许创建一个对象。
实现方式
-
饿汉,懒汉
-
DCL
// 1. 正常方法 public static Singleton getInstance() { if (instance == null) //1 instance = new Singleton(); //2 return instance; //3 } // 2. synchronized 方法 public static synchronized Singleton getInstance() { if (instance == null) //1 instance = new Singleton(); //2 return instance; //3 } // 3. synchronized同步块 public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { instance = new Singleton(); } } return instance; } // 4. DCL(双重检查加锁) public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 if (instance == null) //2 instance = new Singleton(); //3 } } return instance; } // 5. DCL-plus --> volatile public class SingletonDCLPlus { // 防止指令重排 volatile private volatile static SingletonDCLPlus singleton; private SingletonDCLPlus() { System.out.println("------instance singleton-----"); } // 第4步 构造对象时可能发生指令重排。 // allocate 内存空间 => singleton // constr 初始化我们对象。 // singleton = new SingletonDCLPlus(); public static SingletonDCLPlus getInstance() { // 1.判断no-null if (singleton == null) { // 2.获取锁 synchronized (SingletonDCLPlus.class) { // 3.双重校验 if (singleton == null) { //4.构造对象 singleton = new SingletonDCLPlus(); } } } return singleton; } }
-
第1种 : 这个实现只适合于单线程,不能保证只创建一个实例
-
第2种: 第一次调用方法时才需要同步 - 即成功创建了instance后是不需要同步的,实现同步的代价昂贵
-
第3种: 当第二个线程进入
synchronized
块时,它并没有检查instance
是否非null
-
第4种:
-
在第2步的第二次检验,理论上是完美的,但是由于Java 平台内存模型
-
在Java内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序它不会影响单线程的运行结果,但是对多线程会有影响。Java提供volatile来保证一定的有序性。最著名的例子就是单例模式里面的DCL(双重检查锁)
-
进一步解释需要来研究下 第3步
instance = new Singleton()
// 创建对象可以分解为如下的3行伪代码 // memory=allocate(); //1:分配对象的内存空间ctorInstance(memory); // constructor-memory;//2:初始化对象 // instance->memory; //3:设置instance指向刚分配的内存地址 // 上面3行代码中的2和3之间,可能会被重排序导致先3后2, mem = allocate(); //分配内存. instance = mem; //标记非空,但是没有初始化. constructorSingleton(instance);//构建实例.
会有将
instance
引用返回给一个构造完整但部分初始化了的Singleton
对象的情况发生
-
-
第5种:基于第4种 ,所以需要加
volatile
关键字防止重排序
-
-
静态内部类
public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }
-
枚举
- 代码
class Singleton{ // something ... } public enum EnumSingleton { INSTANCE; private Singleton instance; // 枚举中构造函数默认是private EnumSingleton() { instance = new Singleton(); } public Singleton getInstance() { return instance; } } // 测试获取枚举单例 class TestEnumSingleton { public static void main(String[] args) { System.out.println(EnumSingleton.INSTANCE.getInstance()); } } //输出 // Singleton@7ea987ac
-
特点
- 只要 EnumSingleton.INSTANCE.getInstance() 即可获得所要单实例
- 只有这种方式能不被反射和序列化破坏单例
- 线程安全
-
单例是如何被保证
-
在枚举中我们明确了构造方法默认被限制为私有 在我们访问枚举实例时会自动执行构造方法
-
同时每个枚举实例都是
public static final
类型的,也就表明只能被实例化一次,也就是说INSTANCE被保证只实例化一次 -
详情可以自行查看 反编译 EnumSingleton.class后的枚举类 javap -p EnumSingleton.class
> javap -p EnumSingleton.class Compiled from "EnumSingleton.java" public final class EnumSingleton extends java.lang.Enum<EnumSingleton> { public static final EnumSingleton INSTANCE; private Singleton instance; private static final EnumSingleton[] $VALUES; public static EnumSingleton[] values(); public static EnumSingleton valueOf(java.lang.String); private EnumSingleton(); public Singleton getInstance(); static {}; }
枚举实际上是一个java类,并且是一个final class,这也符合枚举不可以被继承的特点。
枚举中INSTANCE都被public static final修饰,以维护不可变性 ,并且是公开的
构造函数被声明成private的
-
工厂模式
转自:Danny Chen
简单工厂(Simple Factory)
-
这个模式本身很简单而且使用在业务较简单的情况下。一般用于小项目或者具体产品很少扩展的情况(这样工厂类才不用经常更改)。它由三种角色组成:
- 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。。
- 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。
- 具体产品角色:工厂类所创建的对象就是此角色的实 例。在java中由一个具体类实现
来用类图来清晰的表示下的它们之间的关系:
graph TD FactoryClient-.依赖.->SimpleFactory FactoryClient-.依赖.->AbstractProduct SimpleFactory-.依赖.->AbstractProduct ConcreteProductA--继承-->AbstractProduct ConcreteProductB--继承-->AbstractProduct -
特点
- 客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品;
- 但是工厂类不太理想,因为每增加一个产品,都要在工厂类中增加相应的判断逻辑,这显自然是违背开闭原则的 (只有一个简单的工厂却要生产很多产品)
工厂方法(Factory Method)
-
抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
-
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
-
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
-
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
来用类图来清晰的表示下的它们之间的关系:
graph TD FactoryClient-.依赖.->AbstractFactory FactoryClient-.依赖.->AbstractProduct AbstractFactory-.依赖.->AbstractProduct ConcreteFacotryA--继承-->AbstractFactory ConcreteFacotryB--继承-->AbstractFactory ConcreteProductA--继承-->AbstractProduct ConcreteProductB--继承-->AbstractProduct ConcreteFacotryA-.依赖.->ConcreteProductA ConcreteFacotryB-.依赖.->ConcreteProductB没办法 ,用mermaid画图真是太丑了 ,不能调位置 尴尬 ……
- 特点
- 当新的产品需要生产时 ,新建一个具体工厂继承抽象工厂,而不用修改任何一个类 , 工厂方法模式是完全符合开闭原则的!
- 但是当产品种类非常多时,就会出现大量的与之对应的工厂类,这不应该是我们所希望的。所以建议在这种情况下使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现
抽象工厂(Abstract Factory)
-
产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。
BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。
同理BmwBussinessCar和BenzBusinessCar也是一个产品族。
-
产品树 :BmwCar和BenzCar就是两个产品树(产品层次结构);
可以这么说,它和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。 而且使用抽象工厂模式还要满足一下条件:
- 系统中有多个产品族,而系统一次只可能消费其中一族产品
- 同属于同一个产品族的产品一起使用。
-
特点
-
如果需要增加新的产品树,那么就要新增三个产品类,并且要修改三个工厂类。这样大批量的改动是很丑陋的做法。
-
简单工厂通过反射 改进 抽象工厂及其子工厂
/** * 简单工厂通过反射 改进 抽象工厂及其子工厂 * @author Administrator * */ class Driver3{ public static BenzCar createBenzCar(String car) throws Exception { return (BenzCar) Class.forName(car).newInstance(); } public static BmwCar createBmwCar(String car) throws Exception { return (BmwCar) Class.forName(car).newInstance(); } public static AudiCar createAudiCar(String car) throws Exception { return (AudiCar) Class.forName(car).newInstance(); } } //客户端 public class SimpleAndAbstractFactory { public static void main(String[] args) throws Exception { AudiCar car = Driver3.createAudiCar("com.java.pattendesign.factory.AudiSportCar"); car.drive(); } }
-
模板方法
特点
-
一般用final标记一个模板流程,然后由各子类分别实现其个性化的子流程逻辑
-
JdbcTemplate
-
TransactionTemplate
-
模板方法模式是一种代码复用的基本技术
代码
public abstract class TemplateClass {
/**
*
* @desc
* 模板方法,用来控制流程
* 申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
* templateMethod方法本身和3个原始操作解耦了
* @return void
*/
final void templateMethod(){
step1();
step2();
step3();
}
/**
* @desc 将step2()声明为抽象方法,具体操作由子类实现
* @return void
*/
abstract void step2();
void step1(){
System.out.println("第一步...");
}
void step3(){
System.out.println("第三步...");
}
}
// 子类实现A来了
public class ConcreteClassA extends TemplateClass{
void step2() {
System.out.println("ConcreteClassA step2...");
}
}
// 子类实现B来了
public class ConcreteClassB extends TemplateClass{
void step2() {
System.out.println("ConcreteClassB step2...");
}
}
// Test
public class Test {
public static void main(String[] args) {
// 通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,
// 实现了反向控制 符合“开闭原则”
ConcreteClassA a = new ConcreteClassA();
a.templateMethod();
}
}
建造者
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景
- 复杂构建
- springMVC 中 通过 UriComponentsBuilder 构建 UriComponents
代码
public class Builder {
static class Student{
String name = null ;
int number = -1 ;
String sex = null ;
int age = -1 ;
String school = null ;
//构建器,利用构建器作为参数来构建Student对象
static class StudentBuilder{
String name = null ;
int number = -1 ;
String sex = null ;
int age = -1 ;
String school = null ;
public StudentBuilder setName(String name) {
this.name = name;
return this ;
}
public StudentBuilder setNumber(int number) {
this.number = number;
return this ;
}
public StudentBuilder setSex(String sex) {
this.sex = sex;
return this ;
}
public StudentBuilder setAge(int age) {
this.age = age;
return this ;
}
public StudentBuilder setSchool(String school) {
this.school = school;
return this ;
}
public Student build() {
return new Student(this);
}
}
public Student(StudentBuilder builder){
this.age = builder.age;
this.name = builder.name;
this.number = builder.number;
this.school = builder.school ;
this.sex = builder.sex ;
}
}
public static void main( String[] args ){
Student a =
new Student.StudentBuilder()
.setAge(13)
.setName("LiHua")
.build();
Student b =
new Student.StudentBuilder()
.setSchool("sc")
.setSex("Male")
.setName("ZhangSan")
.build();
}
}
代理模式
静态代理
// 一份找对象的协议
public interface IProtocol {
void findGril();
void success();
}
// 我想找对象,签了协议
public class Me implements IProtocol {
@Override
public void findGril() {
System.out.println("我准备找一个女朋友");
}
public void success() {
System.out.println("我找到了一个女朋友");
}
}
/**
* 和我签订协议的 【婚恋代理】
*/
public class Proxy implements IProtocol {
private IProtocol me;
private int money;
public Proxy(IProtocol me,int money) {
this.me = me;
this.money = money;
}
@Override
public void findGril() {
if (money >= 500) {
System.out.println("你交钱了,要介绍一个白富美");
me.findGril();
} else {
System.out.println("请先缴纳红娘服务费");
}
}
@Override
public void success() {
System.out.println("我需要验证是否符合我的要求 ^_^");
me.success();
System.out.println("开始和女孩交往");
}
}
// 测试类
public class TestProxy {
public static void main(String[] args) {
// 创建目标类
IProtocol target = new Me();
// 创建代理类
Proxy proxy = new Proxy(target, 666);
// 调用代理方法
proxy.findGril();
System.out.println("-----------------------");
proxy.success();
}
}
动态代理
jdk的实现方式
// jdk的实现方式
package proxy.dynamic.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 动态代理: 可以代理多类型对象
* 实质: 在运行期间通过代码生成一个实现与目标类同一个接口的类
* 局限: 只能代理实现了接口的类
*/
public class JDKDynamicProxy {
// 持有目标类型句柄
private Object target;
/**
* 根据目标类实现的接口生成对应代理类的实例
* @param target
* @return
*/
public Object getJDKProxy(Object target) {
this.target = target;
/**
* classLoader: 目标类的类加载器
* interfaces: 目标类实现的接口组成数组
* invocationHandler: 实现InvocationHandler接口的实现类实例
*/
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyHandler()
);
}
/**
* 通过代理对象调用目标类方法的回调类
*/
private class MyHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println(proxy.getClass());
// System.out.println(method.getName());
// if (args != null) {
// System.out.println(Arrays.asList(args));
// }
Logger.info("执行了"+method.getName()+"方法之前");
Object result = method.invoke(target, args);
Logger.info("执行了"+method.getName()+"方法之后");
return result;
}
}
}
proxy是真实对象的真实代理对象,invoke方法可以返回调用代理对象方法的返回结果,也可以返回对象的真实代理对象(com.sun.proxy.$Proxy0)。
CGLIB的实现方式
// CGLIB的实现方式
package com.aop.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* CGLIB的代理类
*/
public class CGLIBProxy {
private Object target;
public Object createCGLIBProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CallBackClass());
return enhancer.create();
}
private class CallBackClass implements InvocationHandler {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println(o.getClass());
System.out.println(method.getName());
if (objects!=null) {
System.out.println(Arrays.toString(objects));
}
return method.invoke(target, objects);
}
}
}
测试类
// 测试类
public class TestDynamicProxy {
public static void main(String[] args) {
// 1.JDKDynamicProxy
// 创建目标对象
UserDAO userDao = new UserDAOImpl();
// 创建代理对象[jdk动态代理只能强转为接口类,因为它是与UserDAOImpl同级别(同根)的类]
UserDAO proxy = (UserDAO) new JDKDynamicProxy().getJDKProxy(userDao);
proxy.addUser("wesley");
// 2. createCGLIBProxy没有实现接口的类的代理
// 创建目标对象
UserDAOImpl impl = new UserDAOImpl();
// 创建cglib代理
UserDAOImpl cglibProxy = (UserDAOImpl) new CGLIBProxy().createCGLIBProxy(impl);
String result = cglibProxy.addUser("wesley");
System.out.println(result);
}
}
AOP
-
spring声明式事务 @Transactional
-
声明式异常 @ControllerAdvice @ExceptionHandler
-
@Async
在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional. 例如: 方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。 方法B,使用了@Async来标注, B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。
原型模式(Prototype)
原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的。Cloneable接口一个方法都没有,只是一个标记作用。方法是覆盖clone()方法。
特点
- 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多。
- 逃避构造函数的约束。直接在内存中拷贝,构造函数不会执行。
浅拷贝
内部的数组和引用对象不会被拷贝,其他的原始类型比如int、char等都会被copy- 如果不实现该接口而直接调用clone()方法会抛出
CloneNotSupportedException
异常 - 无论是哪种拷贝
- 对任何的对象x,都有x.clone() != x,即克隆对象与原对象不是同一个对象
- 对任何的对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原对象的类型一样。
- 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立
浅拷贝和深拷贝
-
浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值
-
深拷贝:不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值
-
解析图:
graph LR obj((obj)) --原始类型--> int obj((obj)) --引用类型--> User shallow-copy((shallow-copy)) --引用类型--> User shallow-copy((shallow-copy)) --原始类型--> intcopy obj-.clone to .-shallow-copy graph LR obj((obj)) --原始类型--> int obj((obj)) --引用类型--> User deep-copy((deep-copy)) --引用类型--> Usercopy deep-copy((deep-copy)) --原始类型--> intcopy obj-.clone to .-deep-copy
装饰模式(Decorator)
和代理模式的区别
-
代理模式中类的关系不需要客户端去配置,客户只需要使用代理类就ok了。但是装饰者模式中,类之间的关系需要客户指定。
-
使用的场景不一样。
比如IO流使用的装饰者模式,可以层层增加功能。
代理模式一般只用于增加特殊的功能,并不用于多层嵌套。 -
模式区别除了实现外,最重要的是模式的目的性。代理模式与装饰器模式最根本区别是两者的目的不同,而不在具体的实现差异。
UML图
graph BT ContreteComponent--继承-->Component Decorator--继承-->Component Decorator--引用-->Component ContreteDecoratorA--继承-->Decorator ContreteDecoratorB--继承-->Decorator-
Component: 抽象构件。
-
定义一个对象接口,可以给这些对象动态地添加职责。
-
每个组件都可以单独使用,或者被装饰者包装起来使用
-
-
ConcreteComponent:具体构件。
- 定义了一个具体的对象,也可以给这个对象添加一些职责。
-
Decorator: 抽象装饰类。
- 继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。
- 每个装饰者都有一个组件,也就是说 装饰者有一个实例变量保持着Component的引用
-
ConcreteDecorator:具体装饰类,起到给Component添加职责的功能
代码
/**
* 抽象构件角色
*/
public abstract class Component {
public abstract void operation();
}
/**
* 具体构件角色
*/
public class ConcreteComponent extends Component {
@Override
public void operation() {
System.out.println("具体构件的操作");
}
}
/**
* 装饰角色
*/
public abstract class AbstractDecorator extends Component {
protected Component component;
public AbstractDecorator(Component component){
this.component = component;
}
@Override
public void operation() {
if(component != null){
component.operation();
}
}
}
/**
* 具体装饰角色A
*/
public class ConcreteDecoratorA extends AbstractDecorator {
//本类的独有功能
private String addedState;
//首先运行原Component的operation(),再执行本类的功能,相当于对原Component进行了装饰
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation(){
super.operation();
addedState = "New State";
System.out.println("具体装饰对象A的操作。" + addedState);
}
}
/**
* 具体装饰角色B
*/
public class ConcreteDecoratorB extends AbstractDecorator {
//首先运行原Component的operation(),再执行本类的功能,相当于对原Component进行了装饰
@Override
public void operation() {
super.operation();
AddedBehavior();
System.out.println("具体装饰对象B的操作");
}
public ConcreteDecoratorB(Component component) {
super(component);
}
//本类的独有功能
private void AddedBehavior() {
System.out.println("我是具体装饰对象B的独有方法");
}
}
/**
* 客户端代码
*/
public class Demo4 {
public static void main(String[] args) {
Component c = new ConcreteComponent();
ConcreteDecoratorA d1 = new ConcreteDecoratorA(c);
ConcreteDecoratorB d2 = new ConcreteDecoratorB(d1);
// d1.operation();
d2.operation();
/*运行输出结果:
具体对象的操作
具体装饰对象A的操作。New State
我是具体装饰对象B的独有方法
具体装饰对象B的操作
*/
}
}
特点
-
装饰者和被装饰者对象有相同的超类型
-
可以用多个装饰者包装一个组件
-
能动态地给一个对象增加功能,这些功能也可以动态地被撤销。
-
动态地给一个对象添加一些额外的功能。就增加功能来说,Decorator模式相比生成子类更为灵活。
-
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时使用
-
InputStream=>FileInputStream=>BufferedInputStream
InputStreamReader
策略(Strategy)
定义
-
策略模式是一种简单常用的模式,我们在进行开发的时候,会经常有意无意地使用它,一般来说,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的情况比较多。
-
简单来说策略模式就像有几套方案,根据条件来确定用哪套方案
类图和工厂模式很像
graph TD StrategyClient-.调用.->Context(Context环境) Context--引用-->Strategy ConcreteStrategyA--继承-->Strategy ConcreteStrategyB--继承-->Strategy ConcreteStrategyC--继承-->Strategy代码
// 抽象策略类
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}
// 具体策略类
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
//相关的业务a
}
}
// 环境角色类
public class Context {
//持有一个具体策略的对象
private Strategy strategy;
/**
* 构造函数,传入一个具体策略对象
* @param strategy 具体策略对象
*/
public Context(Strategy strategy){
this.strategy = strategy;
}
/**
* 策略方法
*/
public void contextInterface(){
strategy.strategyInterface();
}
}
public class StrategyClient {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
Strategy strategy = new ConcreteStrategyA();
//创建环境
Context context = new Context(strategy);
//计算结果
V result = context.contextInterface(param);
System.out.println("实现策略 ConcreteStrategyA 最终结果为:" + result);
}
}
特点
- 必须对 客户端(调用者)暴露所有的策略类,因为使用哪种策略是由客户端来决定的
- 因此,客户端应该知道有什么策略,并且了解各种策略之间的区别
- 以上就有点违背了最少知识原则
适配器(Adapter)
WHAT
-
适配器模式就是将一个类的接口,转换成客户期望的另一个接口
-
适配器让原本接口不兼容的类可以合作无间
-
类适配 ,对象适配器 ,接口适配器
-
其中
接口适配器
- 就是用抽象类伪实现接口中的方法,然后由子类再去覆盖其中某些功能
- 应用场景:不想实现接口中的所有方法
结构图
以下是对象适配器
graph BT Adapter-.实现目标接口.->Target Adapter--组合引用,所有请求都委托给被适配者-->Adaptee Client--调用目标接口-->Target代码
//首先定义两个不兼容的接口
public interface USB {
void useUSB();
}
public interface PS2 {
void usePS2();
}
// 实现类就省略了
// 1.对象适配器 ,客户需要USB接口 而我们只有PS2接口
public class USB2PS2Adapter implements USB {
private PS2 ps2;
public USB2PS2Adapter(PS2 ps2) {
this.ps2 = ps2;
}
@Override
public void useUSB() {
ps2.usePS2();
}
// 客户来测试接口
public static void main(String[] args) {
USB2PS2Adapter adapter = new USB2PS2Adapter(new PS2());
adapter.useUSB();
}
}
// 2. 类适配器 ,注意java多继承限制
public class Adapter extends PS2Impl implements USB {
@Override
public void useUSB() {
usePS2();
}
public static void main(String[] args) {
// 创建USB到PS2接口的适配对象
Adapter adapter = new Adapter();
// 直接用适配器上的usb接口
adapter.useUSB();
}
}
// 3.接口适配器,不实现所有接口方法
/**
* 定义端口接口,提供通信服务
*/
public interface Port {
// 远程SSH端口22
public void SSH();
// 网络端口80
public void NET();
// Tomcat容器端口8080
public void Tomcat();
// Mysql数据库端口3306
public void Mysql();
// Oracle数据库端口1521
public void Oracle();
// 文件传输FTP端口21
public void FTP();
}
/**
* 定义抽象类实现端口接口,但是什么事情都不做
*/
public abstract class Wrapper implements Port{
@Override
public void SSH(){};
@Override
public void NET(){};
@Override
public void Tomcat(){};
@Override
public void Mysql(){};
@Override
public void Oracle(){};
@Override
public void FTP(){};
}
/**
* 提供聊天服务
* 需要网络和文件传输功能
*/
public class Chat extends Wrapper{
@Override
public void NET(){ System.out.println("Hello world!"); };
@Override
public void FTP(){ System.out.println("File upload succeddful!"); };
}
/**
* 网站服务器
* 需要Tomcat容器,Mysql数据库,网络服务,远程服务
*/
public class Server extends Wrapper{
@Override
public void SSH(){ System.out.println("Connect success!"); };
@Override
public void NET(){ System.out.println("Hello WWW!"); };
@Override
public void Tomcat(){ System.out.println("Tomcat 9 is running!"); };
@Override
public void Mysql(){ System.out.println("Mysql is running!"); };
}
/**
* 运行器
* 运行聊天服务和服务器
*/
public class Start {
private static Port chatPort = new Chat();
private static Port serverPort = new Server();
public static void main(String[] args) {
// 聊天服务
chatPort.FTP();
chatPort.NET();
// 服务器
serverPort.Mysql();
serverPort.SSH();
serverPort.Tomcat();
serverPort.NET();
}
}
// 运行结果
File upload succeddful!
Hello world!
Mysql is running!
Connect success!
Tomcat 9 is running!
Hello WWW!
观察者(Observer)
WHAT
- 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
- 订阅-发布 : 发布一条信息 => 公众号有新的动态
结构图
graph BT; Subject(Subject<br/>**********<br/>+notify方法<br/>+attach-Observer o<br/>+detach-Observer o)--+subject-->Observer(Observer<br/>**********<br/>+update方法) ConcreteObserver--实现or继承-->Observer ConcreteSubject--实现or继承-->Subject角色
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
代码
微信公众号发布通知举例
/**
* 定义主题接口
*/
public interface Subject {
// 添加观察者
boolean addObserve(Observer observer);
// 移除观察者
boolean removeObserver(Observer observer);
// 通知所有观察者
void notice(String message);
}
/**
* 微信公众号
*/
public class WechatPublicAccount implements Subject {
private List<Observer> observerList = new ArrayList<>();
@Override
public boolean addObserve(Observer o) {
return observerList.add(o);
}
@Override
public boolean removeObserver(Observer o) {
return observerList.remove(o);
}
@Override
public void notice(String message) {
for (Observer o : observerList) {
o.update(message);
}
}
}
/**
* 抽象观察者
*/
public interface Observer {
// 定义更新自己的方法
void update(String message);
}
/**
* 微信公众号订阅用户
*/
public class WechatUser implements Observer {
private String name;
public WechatUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name+"->"+message);
}
}
/**
* 测试观察者设计模式类
*/
public class TestObserver {
public static void main(String[] args) {
// 创建一个公众号
Subject wechatPublicAccount = new WechatPublicAccount();
// 创建微信用户
WechatUser user1 = new WechatUser("张三");
WechatUser user2 = new WechatUser("李四");
WechatUser user3 = new WechatUser("王五");
// 订阅公众号
wechatPublicAccount.addObserve(user1);
wechatPublicAccount.addObserve(user2);
wechatPublicAccount.addObserve(user3);
// 公众号发出更新通知
wechatPublicAccount.notice("公众号更新了国庆放假通知......");
// 退订
wechatPublicAccount.removeObserver(user2);
System.out.println("==========================");
// 公众号发出更新通知
wechatPublicAccount.notice("公众号更新了国庆放假通知......");
}
}
组合模式
类图
两种不同实现 : 透明模式和安全模式
- 安全模式
- 透明模式
特点和角色
- 定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理
- 用于将多个对象组合成树形结构以表示“整体-部分”的结构层次。对叶子对象和组合对象的使用具有一致性。
- Component :组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
- Leaf:叶子对象。叶子结点没有子结点。
- Composite:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
- 叶子节点和容器对象都实现Component接口,这也是能够将叶子对象和容器对象一致对待的关键所在。
- 两种不同实现 : 透明模式和安全模式
- 透明模式需要通过判断getChild()返回值来确定是叶子节点还是树枝节点
代码
package composite;
public abstract class Component {
public void doSomething(){
// 业务逻辑
}
}
public class Composite extends Component {
private ArrayList<Component> componentArrayList = new ArrayList<>();
public void add(Component component){
this.componentArrayList.add(component);
}
public void remove(Component component){
this.componentArrayList.remove(component);
}
public ArrayList<Component> getChild(){
return this.componentArrayList;
}
}
public class Leaf extends Component {
@Override
public void doSomething() {
System.out.println("叶子节点覆盖父类的方法");
}
}
public class Client {
public static void main(String[] args) {
// 创建一个根节点
Composite root = new Composite();
root.doSomething();
Composite branch = new Composite();
Leaf leaf = new Leaf();
root.add(branch);
branch.add(leaf);
}
public static void display(Composite root){
for(Component c :root.getChild()){
if(c instanceof Leaf){
c.doSomething();
}else{
display((Composite) c);
}
}
}
}
标签:之禅,--,void,Component,接口,class,设计模式,public 来源: https://www.cnblogs.com/skystarry/p/14940757.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。