目录
记录设计模式的相关内容,逐步完善内容。
1.设计模式的七大原则
1.1开闭原则
是什么?对修改关闭,对扩展开放
核心思想:面向抽象编程
优点:提高系统可复用性和可维护性
/**
* 开闭原则
* 思想:对修改关闭,对扩展开放 面向抽象编程
* 好处:提高系统的可复用性和可维护性
*/
public class OpenWritePolicy {
/**
* 测试接口类
*/
interface MyTestInterface{
String getMyName();
BigDecimal getMyMoney();
}
public class MyTestInterfaceImpl implements MyTestInterface{
private String name;
private BigDecimal money;
public MyTestInterfaceImpl(String name,BigDecimal money) {
this.name = name;
this.money =money;
}
@Override
public String getMyName() {
return this.name;
}
@Override
public BigDecimal getMyMoney() {
return this.money;
}
}
//当需要对金钱修改的时候就要做扩展
public class MyTestExtendInterfaceImpl extends MyTestInterfaceImpl{
public MyTestExtendInterfaceImpl(String name, BigDecimal money) {
super(name, money);
}
//此代码违反了里氏替换原则。不要重写父类方法,要扩展父类方法
@Override
public BigDecimal getMyMoney() {
return super.getMyMoney();
}
public BigDecimal getAddMyMoney() {
return super.getMyMoney().add(new BigDecimal(100));
}
}
}
1.2依赖倒置原则
是什么?抽象不应该依赖细节,细节应该依赖抽象
作用?用于降低类的解耦,提高系统稳定性,提高代码可读写和可维护性,降低修改程序带来的风险。
思想:面向接口编程。先顶层后细节
1.3单一职责原则
是什么?不要存在一个以上导致类变更的原因,负责一个职责
作用?降低类的复杂度;提高类的可读性;提高系统可维护性;降低变更风险
实际使用中:做到单一职责很难,因为代码中存在依赖组合聚合关系,尽可能的让接口和方法保持单一职责。
1.4接口隔离原则
是什么?接口和接口之间应该是隔离的,而不是相互依赖。同时也叫最小接口原则。
作用?提高代码可读性,可扩展性和可维护性
1.5迪米特原则
是什么?也叫最少知道原则(LKP least knowledge principle)只和朋友说话,不与陌生人说话。
作用?降低类与类的耦合
1.6 里氏替换原则
是什么?子类可以扩展父类方法,不要修改父类原有的功能
作用?加强代码健壮性
1.7 合成复用原则
是什么?尽量使用对象组合或者对象聚合,而不是用继承关系达到代码复用 黑盒复用
作用?提高代码复用
1.8每个原则一句话总结
原则 | 归纳 | 作用 |
开闭原则 | 对修改关闭,多扩展开放 | 降低维护修改后的风险 |
依赖倒置原则 | 高层不应该依赖底层,细节依赖抽象,抽象不依赖细节 | 便于代码结构的可扩展性 |
单一职责原则 | 一个类只干一件事 | 提高代码可读性 |
接口隔离原则 | 一个接口只干一件事 | 功能解耦,高内聚、低耦合 |
里氏替换原子 | 子类重写方法功能发生改变,不要影响父类方法 | 防止继承泛滥 |
迪米特原则 | 最少知道,不该知道的不知道 | 减少代码臃肿 |
合成复用原则 | 尽量使用组合实现代码复用,而不是使用继承 | 降低代码耦合 |
2.责任链设计模式总结
是什么?将一个链中每个节点都看作一个对象,自动维护下一个节点。行为型设计模式
场景:多条件的校验;流程通过一类的
角色:Handler 抽象处理者
ConcreateHandler 具体实现者
作用:代码解耦,代码复用,代码扩展
源码中表现:Filter过滤器的使用相当于责任链的抽象处理;有一个FilterChain类
还有就是常用的StringBuilder,StringBuffer()的append()方法。
优点:请求和处理解耦
缺点:责任链太长或者处理时间太长,会影响性能。节点对象的循环引用会造成死循环。
/**
* 责任链设计模式:
* 将多个对象成为一个链进行处理
* 优点:降低系统耦合度 多个对象互不影响
* 增强系统灵活性 可以随时去掉和新增某一个链
* 增强系统可扩展性 可以做多个扩展
* 提高系统可维护性
* 缺点:
* 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
* 请求可能没有被处理
*/
public class MyChinTest {
public static void main(String[] args) {
ChainHandler a = new ConcreteChainHandlerA();
ChainHandler b = new ConcreteChainHandlerB();
a.next(b);
a.handler("测试责任链");
}
}
/**
* 抽象处理者,抽象类
*/
abstract class ChainHandler{
ChainHandler chainHandler;
/**
* 抽象方法
* @param msg
*/
public abstract void handler(String msg);
/**
* 下一个处理者
* @param chainHandler
*/
public void next(ChainHandler chainHandler) {
this.chainHandler = chainHandler;
}
}
//具体处理者
class ConcreteChainHandlerA extends ChainHandler{
@Override
public void handler(String msg) {
System.out.println("处理者A开始处理");
System.out.println("处理者A处理完成,信息为"+msg);
if(chainHandler!=null) {
chainHandler.handler(msg);
}
}
}
//具体处理者
class ConcreteChainHandlerB extends ChainHandler{
@Override
public void handler(String msg) {
System.out.println("处理者B开始处理");
System.out.println("处理者B处理完成,信息为"+msg);
if(chainHandler!=null) {
chainHandler.handler(msg);
}
}
}
3.建造者设计模式
是什么?创建型设计模式。将对象的创建过程和表示分离,使同样的创建过程可以创建不同的表示。核心点:构建和表示分离;创建不同的表示。
和工厂模式的区别:建造者使创建的复杂的对象,分离创建和表示。
应用场景:相同的方法,不同的顺序,产生不同的结果
角色:(1)产品:构建的对象
(2)抽象建造者:
(3)具体建造者
(4)调用者
实际使用中,我们可以使用匿名内部类进行实现;
源码中的实现:mybaits生成的类中就有Builder的存在。
SpringBuilder的append()方法,同理StringBuffer的append()方法。
Spring中的BeanDefinitionBuilder类
优点:更注重方法的调用顺序,工厂模式注重创建对象。
构建和表示分离。扩展性好
缺点:产品变化,构造者很多情况跟着变化,后期维护成本较大。
public class BuilderPattern {
public static void main(String[] args) {
Builder builder = new Builder()
.addName("test")
.addPrices(new BigDecimal(3))
.addDesc("test");
Product product = builder.build();
System.out.println(product.toString());
}
}
/**
* 产品
*/
class Product{
//产品名称
String name;
//产品价格
BigDecimal prices;
//产品描述
String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrices() {
return prices;
}
public void setPrices(BigDecimal prices) {
this.prices = prices;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", prices=" + prices +
", desc='" + desc + '\'' +
'}';
}
}
//建造者
class Builder{
private Product product = new Product();
public Builder addName(String name) {
product.setName(name);
return this;
}
public Builder addPrices(BigDecimal prices) {
product.setPrices(prices);
return this;
}
public Builder addDesc(String desc) {
product.setDesc(desc);
return this;
}
public Product build() {
return product;
}
}
4.建造者和责任链组合
通常行为型设计模式和创建型进行组合;行为型注重的是实现,创建型在于对象的创建
责任链的这个链式组装比较长的情况下,就比较麻烦,代码就会很臃肿,可以使用建造者设计模式,来进行创建和表示的分离。
public class ChainTest {
public static void main(String[] args) {
// ChainHandler a = new MyChainConcreteHandlerA();
// ChainHandler b = new MyChainConcreteHandlerB();
// a.next(b);
// a.handler("测试");
ChainHandler.Builder builder = new ChainHandler.Builder()
.addBuilder(new MyChainConcreteHandlerA())
.addBuilder(new MyChainConcreteHandlerB());
ChainHandler chainHandler = builder.build();
chainHandler.handler("测试");
}
}
abstract class ChainHandler{
ChainHandler chinHandler;
public abstract void handler(String msg);
public void next(ChainHandler chinHandler) {
this.chinHandler = chinHandler;
}
//静态内部类
static class Builder{
//产品
private ChainHandler head;
private ChainHandler tail;
public Builder addBuilder(ChainHandler chainHandler) {
if(this.head==null) {
this.head = this.tail = chainHandler;
return this;
}
this.head.next(chainHandler);
return this;
}
public ChainHandler build() {
return this.head;
}
}
}
class MyChainConcreteHandlerA extends ChainHandler {
@Override
public void handler(String msg) {
System.out.println(msg);
if(chinHandler!=null) {
chinHandler.handler(msg);
}
}
}
class MyChainConcreteHandlerB extends ChainHandler {
@Override
public void handler(String msg) {
System.out.println(msg);
if(chinHandler!=null) {
chinHandler.handler(msg);
}
}
}
5.代理模式
是什么?为其他对象提供一种代理,以控制对这个对象的访问;属于结构型设计模式。
场景:中介;快递;事务日志;日志监听等
作用:1.保护目标对象
2.增强目标对象
角色:抽象主题角色
真实对象角色
代理对象角色
优点:将代理对象和真实对象分离
代码解耦,扩展性好。
保护目标对象
增强目标对象
缺点:增加一个代理对象,会让处理请求变慢。
增加系统复杂度。
在代码中,一般代理会被理解为代码增强,实际上就是在原有代码前后增加一些代码逻辑,而使调用者无感知。代理模式分为静态代理和动态代理。
/**
* 代理模式测试
* 1.是什么?为一组对象生成代理对象,以控制对象访问
* 2.作用:1.保护目标对象 2.增强目标对象
* 3.角色:抽象主题类;
* 真实主题类:
* 代理主题类
*/
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.request();
}
}
interface ISubject{
void request();
}
class RealSubject implements ISubject{
@Override
public void request() {
System.out.println("处理请求");
}
}
//代理类
class Proxy implements ISubject {
private ISubject subject;
public Proxy(ISubject subject){
this.subject = subject;
}
@Override
public void request() {
before();
subject.request();
after();
}
private void after() {
System.out.println("after request");
}
private void before() {
System.out.println("before request");
}
}
JDK动态代理和CGLIB动态代理。
jdk动态代理:
原理:Proxy.newProxyInstance(类加载器,接口类,InvocationHandler接口类)
然后回调InvocationHandler的invoke方法。
1.获取被代理对象的引用,并且获取它所有接口,通过反射获取
2.JDK动态代理重新生成一个新的类,同时新的类实现被代理类的所有接口。
3.动态生成java代码,新加的逻辑由一定的逻辑调用
4.编译生成的java代码成.class文件
5.重新加载到JVM中运行。*
/**
* jdk动态代理测试实现
*/
public class JDKProxyTest {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();
MySubject mySubject =jdkProxy.getInstance(new MyRealSubject());
mySubject.request();
}
}
interface MySubject{
void request();
}
class MyRealSubject implements MySubject {
@Override
public void request() {
System.out.println("处理请求");
}
}
class JdkProxy implements InvocationHandler {
private MySubject subject;
public MySubject getInstance(MySubject subject) {
this.subject = subject;
Class<?> clazz = this.subject.getClass();
//实现原理 调用Proxy.newProxyInstance(类加载器,接口类实现,InvocationHandler接口)
return (MySubject) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
method.invoke(this.subject,args);
after();
return null;
}
private void after() {
System.out.println("after 请求");
}
private void before() {
System.out.println("before 之前处理");
}
}
cglib动态代理:
原理实现:Enhancer 代理类,里面传入两个参数,1.当前类 2.设置回调方法MethodInterceptor拦截器
Methodinterceptor里面的intercept方法中:使用methodProxy.invoke方法构造对象。
//需要注意的点,此处引包 import org.springframework.cglib.proxy.MethodInterceptor 是cglib的MethodInterceptor包
class CglibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz) {
//相当于proxy代理类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invoke(o,objects);
after();
return obj;
}
private void after() {
System.out.println("after 请求");
}
private void before() {
System.out.println("before请求");
}
}
JDK是采用读取接口信息;CGLIB是覆盖父类方法,继承的实现。CGLIB生成代理的逻辑复杂,调用效率更高,不需要反射。
6.装饰器模式
是什么?不改变原有对象基础上,动态的给对象添加一些格外的职责。属于结构型设计模式
装饰器提供了比继承更有弹性的替代方案(扩展原有的功能)将功能附加到对象上。
核心是功能扩展。
角色:抽象组件
具体组件
抽象装饰器
具体装饰器。
原理:让装饰器实现和被装饰器相同的接口,使得装饰器与被扩展类类型一致,并在构造器传入该接口对象。
实现:接口类,实现类,抽象装饰器,具体装饰器
抽象装饰器中实现接口类,创建接口类对象,在构造方法中传入接口类,实现接口类方法。
具体装饰器继承抽象装饰器,实现其接口类方法,添加一些格外逻辑。
解决什么实际问题?1.解决煎饼“加码”问题。
2.扩展日志格式输出
源码:I/O操作的相关类。
装饰器和代理模式区别:都是对目标对象增强。装饰器强调自身功能的扩展。 代理强调的是对对象的控制。
优点:对继承的有力补充,比继承灵活,在不改变原有代码基础上,动态给一个功能做扩展,即插即用。遵循开闭原则。
缺点:会出现更多的类,代码,增加代码的复杂性
动态装饰在多层装饰更复杂。
/**
* 装饰器设计模式代码
* 1.被装饰器接口
* 2.被装饰器实现
* 3.抽象装饰器
* 4.抽象装饰器实现
*/
public class DecoratorTest {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Decorator decorator = new ConcreteDecoratorA(component);
//decorator.handler("test");
Decorator b = new ConcreteDecoratorB(decorator);
b.handler("test");
}
}
/**
* 抽象组件
*/
interface Component{
void handler(String msg);
}
//具体组件
class ConcreteComponent implements Component {
@Override
public void handler(String msg) {
System.out.println("处理业务逻辑,处理内容"+msg);
}
}
//抽象装饰器实现被装饰接口
abstract class Decorator implements Component {
private Component component;
/**
* 构造方法,传入组件对象
* @param component
*/
public Decorator(Component component) {
this.component = component;
}
@Override
public void handler(String msg) {
component.handler(msg);
}
}
class ConcreteDecoratorA extends Decorator {
/**
* 构造方法,传入组件对象
*
* @param component
*/
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void handler(String msg) {
//操作一些其他内容
handlerBefore();
super.handler(msg);
handlerAfter();
}
private void handlerAfter() {
System.out.println("A处理一些其他内容");
}
/**
* 处理其他一些内容
*/
private void handlerBefore() {
System.out.println("A处理一些其他内容");
}
}
class ConcreteDecoratorB extends Decorator {
/**
* 构造方法,传入组件对象
*
* @param component
*/
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void handler(String msg) {
//操作一些其他内容
handlerBefore();
super.handler(msg);
handlerAfter();
}
private void handlerAfter() {
System.out.println("B处理一些其他内容");
}
/**
* 处理其他一些内容
*/
private void handlerBefore() {
System.out.println("B处理一些其他内容");
}
}
7.策略模式
是什么?同一行为在不同场景下具备不同得实现
思想:面向对象得继承和多态机制。
角色:上下文角色 抽象策略类 具体策略类
场景:大量if/else
源码:Comparator.compare()比较器
优点:符合开闭原则;
扩展性比较好;
提高算法得保密性和安全性
缺点:调用者必须要知道每个策略实现用途。
策略实现比较多情况下,维护起来比较费劲
代码实现:
/**
* 策略模式
* 是什么?同一行为在不同场景下具备不同得实现
* 思想:面向对象得继承和多态机制。
* 角色:上下文角色
* 抽象策略类
* 具体策略类
*/
public class StrategyTest {
public static void main(String[] args) {
StrategyContent strategyContent = new StrategyContent(new StrategyImplA());
strategyContent.handler("测试");
strategyContent = new StrategyContent(new StrategyImplB());
strategyContent.handler("测试");
}
}
//策略接口类
interface StrategyInterface{
void handler(String msg);
}
//策略实现类
class StrategyImplA implements StrategyInterface{
@Override
public void handler(String msg) {
System.out.println("a请求处理消息"+msg);
}
}
class StrategyImplB implements StrategyInterface {
@Override
public void handler(String msg) {
System.out.println("b处理消息"+msg);
}
}
//上下文
class StrategyContent{
private StrategyInterface strategyInterface;
public StrategyContent(StrategyInterface strategyInterface) {
this.strategyInterface = strategyInterface;
}
public void handler(String msg) {
strategyInterface.handler(msg);
}
}
8.状态模式
1.是什么?允许对象在内部状态发生改变时候改变它的行为,对象看起来修改了它的类。可以理解为状态的切换。 行为型设计模式
状态修改,行为也改变。核心是状态与行为绑定,不同状态对应不同的行为。
主要解决一个对象状态的条件表达式过于复杂的情况。
应用场景:行为随着状态而改变的场景。
一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态。
角色:上下文角色
抽象状态类
具体状态类
这三个看起来是不是和策略模式相同。是的,角色都是一样的,不同的是上下文的处理。
状态模式和责任链模式区别?
责任链强调的不同对象的调用成链,不知道下一个节点处理的内容
状态模式强调的是一个对象的内部转换过程,知道下一个节点处理的内容
状态模式和策略模式的区别?
策略模式每个实现类是独立的。
状态模式实现类是存在关联的。
优点:结构清晰,消除冗余的if-else或者swith case
将状态转换显示化
具备扩展性
缺点:类爆炸
开闭原则不太友好。
代码实现:
public class StateTest {
public static void main(String[] args) {
StateContent content = new StateContent();
content.process();
content.process();
content.cancel();
content.cancel();
}
}
interface StateInterface{
//处理流程
void process(StateContent content);
//撤销流程
void cancel(StateContent content);
}
//新建状态
class NewState implements StateInterface {
//流程处理
@Override
public void process(StateContent content) {
System.out.println("新建处理完成");
content.setState(new FinishState());
}
//撤销操作
@Override
public void cancel(StateContent content) {
System.out.println("撤销成功");
content.setState(new CancelState());
}
}
class FinishState implements StateInterface {
@Override
public void process(StateContent content) {
System.out.println("完成状态切换完成");
}
@Override
public void cancel(StateContent content) {
System.out.println("完成状态下切换新建状态");
content.setState(new NewState());
}
}
class CancelState implements StateInterface{
@Override
public void process(StateContent content) {
System.out.println("撤销状态切换成功");
}
@Override
public void cancel(StateContent content) {
System.out.println("撤销处理成功");
}
}
//环境类
class StateContent{
private StateInterface state;
public StateContent() {
this.state = new NewState();
}
public void process() {
this.state.process(this);
}
public void cancel() {
this.state.cancel(this);
}
public void setState(StateInterface state) {
this.state = state;
}
}
9.适配器模式
是什么?转换接口到另一个接口可以接受。
思想:一个中间层,适配器起着一个转化/委托的作用,将一种接口转为另一种符合需求的接口
场景:电源转换头,手机充电转换头,显示器转换头等。
核心:适配器
业务场景:
已经存在的类,方法和需求不匹配的情况。通常我们都是做适配器类,比如第三方接口上我们会做类参数的转换,本身就是一种适配器。再比如不同的登录方式,用户名密码登录,手机号登录等。
形式:类适配器,接口适配器,方法适配器。
角色:
目标角色
源角色
适配器(Adapter)
适配器主要解决的是功能兼容问题。
源码中表现:IO,JDBC-odbc
10.观察者模式
是什么?一对多的依赖关系,被观察者可被多个观察者对象同时监听,被观察变了观察者跟着变化。属于行为型设计模式。
核心:被观察者和观察者的解耦
角色:抽象被观察者。可以是接口或者类
被观察者实现类.
抽象观察者
观察者实现类
代码实现:
思路:被观察者里面的设计:1.注册 2.移除 3.通知
观察者里面的设计:操作。
优点:
1.观察者和被观察者松耦合,符合依赖倒置原则。
2.分离了表示层和数据处理层。
缺点:观察者数量太多,性能有影响
public class ObserverTest {
public static void main(String[] args) {
//定义几个观察者
Observer a = new ObserveImpl();
Observer b = new ObserveImpl();
//注册到被观察者中
Observable observable = new ObservableImpl();
observable.register(a);
observable.register(b);
observable.handler("test");
}
}
/**
* 观察者接口
*/
interface Observer{
//变更方法
void update(String msg);
}
class ObserveImpl implements Observer{
@Override
public void update(String msg) {
System.out.println("监听到变化,消息为"+msg);
}
}
/**
* 被观察者接口类
*/
interface Observable{
//注册方法
void register(Observer observer);
//移除方法
boolean remove(Observer observer);
//执行方法
void handler(String msg);
//通知方法
void notifyObserve(String msg);
}
/**
* 被观察者实现类
*/
class ObservableImpl implements Observable {
List<Observer> list = new ArrayList<>();
@Override
public void register(Observer observer) {
//注册监听者
list.add(observer);
}
@Override
public boolean remove(Observer observer) {
//移除监听者
if(list.size()==0) {
return false;
}
if(!list.contains(observer)) {
return false;
}
list.remove(observer);
return true;
}
@Override
public void handler(String msg) {
System.out.println("请求处理");
notifyObserve(msg);
}
@Override
public void notifyObserve(String msg) {
for(Observer observer:list) {
observer.update(msg);
}
}
}
11.模板模式
是什么?定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法结构即可重定义该算法的某些特定步骤,属于设计型设计模式。
思想:封装一个固定流程,该流程由几个步骤组成,具体步骤由子类进行不同的实现。
本质:抽象封装流程,具体进行执行。
应用场景:一次性实现算法不变的部分,并将可变行为留给子类实现。
公共行为提取,集中到一个父类中,从而避免代码重复。
角色:
抽象类
具体实现
源码中实现:JDBCTenplate
优点:将相同的处理逻辑抽离到父类中,提高代码复用性。
将不同算法逻辑分离到不同的子类中,通过对子类扩展增加新的行为,提高代码可扩展性。
缺点:每一个抽象类都需要增加一个子类实现,增加系统复杂度。
类膨胀
继承关系,父类修改,所有子类均修改
/**
* 测试模板模式
* 是什么?定义一个操作中的算法框架,将一些步骤延迟到子类中,使得子类不改变框架的情况下可以修改部分步骤
* 思想:抽离出来公共部分,具体步骤子类实现。
* 角色:抽象类,具体实现类
*/
public class TestTemplate {
public static void main(String[] args) {
AbstractClass abc = new ConcreteClassA();
abc.templateMethod();
abc = new ConcreteClassB();
abc.templateMethod();
}
}
abstract class AbstractClass {
protected void step1(){
System.out.println("步骤1执行");
}
protected void step2() {
System.out.println("步骤2执行");
}
protected void step3() {
System.out.println("步骤3执行");
}
//防止子类重写
final void templateMethod() {
step1();
step2();
step3();
}
}
class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("a 实现步骤1");
}
}
class ConcreteClassB extends AbstractClass {
@Override
protected void step2() {
System.out.println("b 实现步骤2");
}
}
12.原型模式
是什么?复制对象创建新的对象,属于创建性模式
思想:复制原型对象,基于内存的二进制流复制
应用场景:
创建对象成本太大
创建对象需要繁琐的数据准备或访问权限。
系统中大量使用该类对象,并且需要给它属性赋值。
实现方式:实现Cloneable接口,重写clone方法。
如果只是调用了clone()方法,编译没问题,运行会报异常:NotSuportedException
浅克隆:只复制第一层的内容,引用不变
深克隆:复制全部对象内容,引用需要深克隆。
角色:
客户
抽象原型
具体原型
优点:提升对象创建时间,性能优良
采用深克隆方式保存对象状态,简化对象创建过程
缺点:需要给每一个类都实现Cloneable接口,并重写clone方法
clone违背了开闭原则
实现比较麻烦。
13.门面模式
是什么?提供一个统一得接口,用来访问子系统中一群接口。
主要特征:定义一个高层接口,让子系统更好得调用,属于结构型设计模式
角色:门面角色 Facade
子系统角色Subsystem
优点:简化了调用过程,不用深入理解系统,以防止给子系统带来风险
减少系统耦合,松散耦合
更好得划分层次,提高安全性
遵循迪米特原则
缺点:不符合开闭原则
增加子系统和扩展子系统,容易造成风险
违背单一职责原则。
代码实现:
/**
* 门面模式
* 是什么:一个高层接口,可以调用多个子系统接口
* 角色:门面角色
* 子系统角色
*/
public class FacadeTest {
@Autowired
private IFacade iFacade;
public static void main(String[] args) {
new FacadeTest().iFacade.handlerSomeThing();
}
}
interface IFacade {
void handlerSomeThing();
}
@Service
class IFacadeImpl implements IFacade{
@Autowired
private ServiceA serviceA;
@Autowired
private ServiceB serviceB;
@Override
public void handlerSomeThing() {
serviceA.doA();
serviceB.doB();
}
}
interface ServiceA{
void doA();
}
@Service
class ServiceImplA implements ServiceA{
@Override
public void doA() {
System.out.println("a做a实现");
}
}
interface ServiceB{
void doB();
}
@Service
class ServiceImplB implements ServiceB{
@Override
public void doB() {
System.out.println("b做b实现");
}
}
14.享元模式
是什么?Flyweight Pattern叫做轻量级模式,是对象池得一种实现。提供了减少对象数量从而改善应用所需得对象结构得方式。
称为:注册式单例模式。
宗旨:共享细粒度对象,将多个对同一对象得访问集中起来,不必为每个访问者创建一个对象,以此降低内存得消耗,属于结构型设计模式。
把一个对象分为内部状态和外部状态,内部状态时不变得,外部状态时变化得,共享不变得部分,打到减少对象数量并节约内存得目的。
应用场景:
共享房源,共享单车,全国社保联网等。
缓冲池得存在。
角色:
1.抽象享元角色
2.具体享元角色
3.享元工厂
优点:减少对象创建,降低内存中对象数量,降低内存,提高效率
减少内存之外得其他资源占用
缺点:关注内外部状态,
使系统、程序得逻辑化
源码中表现:
String,字符串连接池。
Integer
代码如下:
//以下是String得测试
String a = "hello";
String a1 = "hello";
System.out.println(a==a1);//true 共享常量池引用
String a2 = "he"+"llo";
System.out.println(a==a2);///true 本来以为是false,打印结果是true,原因是因为jvm在编译得时候会对此优化,就拼接在一起了
String a3 = "hel"+new String("lo");//false
System.out.println(a==a3);//false 因为new得对象在堆中,堆中又指向lo得引用+hel得引用,所以不一样。
String a4 = new String("hello");
System.out.println(a==a4);//false new String在堆中有引用,引用又指向常量池,两个得引用不一致false
String a5 = a4.intern();
System.out.println(a==a4);//true 此处native方法实现,运行期间动态加入常量池,如果此时常量池中有就复用,否则复制一份到常量池中返回引用
String a6 = "h";
String a7 = "ello";
String a8 = a6+a7;
System.out.println(a==a8);//此处是false,引用不同,a8等于h+ello得两个引用
//以下是Integer得测试
Integer a = Integer.valueOf(100);
Integer b = 100;
//此处结果为true
System.out.println(a==b);
Integer c = Integer.valueOf(10000);
Integer d = 10000;
//此处为false
System.out.println(c==d);
//为什么出现上面结果,主要是享元模式得功劳
//以下是Integer源码得摘抄;其中IntegerCache.low=-128,IntegerCache.high = 127
//所以Integer如果在-128到127范围内,是用得常量池得连接
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
享元模式代码如下:
/**
* 享元模式测试
* 是什么?对象池得一种技术,共享对象,将多个对同一对象得访问集中起来,不必为每个对象创建一个单独得对象
* 角色:抽象享元角色
* 具体享元角色
* 享元工厂
*/
public class FlyweightTest {
public static void main(String[] args) {
FlyweightFactory flyweightFactory = new FlyweightFactory();
IFlyweight iFlyweight =flyweightFactory.getFlyweight("new");
IFlyweight iFlyweight1 = flyweightFactory.getFlyweight("finished");
iFlyweight.operate("a");
iFlyweight1.operate("b");
}
}
//抽象享元工厂
interface IFlyweight{
void operate(String flyweight);
}
class IFlyweightImpl implements IFlyweight {
//公共得东西
private String state;
public IFlyweightImpl(String state) {
this.state = state;
}
@Override
public void operate(String flyweight) {
System.out.println("状态为"+state);
System.out.println("操作得内容"+flyweight);
}
}
class FlyweightFactory{
private Map<String,IFlyweight> map = new HashMap<>();
public IFlyweight getFlyweight(String state) {
if(!map.containsKey(state)) {
IFlyweight iFlyweight = new IFlyweightImpl(state);
map.put(state,iFlyweight);
return iFlyweight;
}
return map.get(state);
}
}
15.组合模式
是什么?Composite Pattern 将单个对象和组合对象用相同得接口表示,使得客户对单个对象和组合对象使用具有一致性。属于结构型设计模式
用来描述整体和部分得关系,将对象组织在树形结构中,顶层节点被称为根节点,根节点下面包含树枝节点和叶子节点。树枝节点又可以包含树枝节点+叶子节点。
应用场景:客户端可以忽略组合对象和单个对象得差异。
对象层次具备整体和部分,呈树形结构。
源码:HashMap 其中Node就是叶子节点,
优点:简化客户端代码,符合开闭原则,清楚定义各个层次得复杂对象,表示全部或部分层次。
缺点:设计比较抽象,限制类型时比较复杂。
16.桥接模式
是什么?又叫桥梁模式Bride Pattern、接口模式、柄体模式。指的将抽象部分与具体实现部分分离,使它们都可以独立变化,属于结构型设计模式。
目的:通过组合得方式建立两个类之间得关系,而不是继承,但又类似多重继承方案。但是多重继承方案又违背了单一职责原则,复用性比较差,桥接方式是比多重继承更好得替代方案。桥接核心在于把抽象与实现解耦。
解耦对象职责。
角色:抽象(Abstraction)
修正抽象
实现
具体实现
源码中表现:Driver
优点:分离抽象和部分具体实现
提高系统得扩展性
符合开闭原则
符合合成复用原则
缺点:增加系统理解和设计难度
需要理清2个维度得变化
代码实现:
/**
* 桥接模式测试
* 是什么?抽象与实现部分分离,通过组合得形式
* 角色:抽象
* 修正抽象
* 实现
* 具体实现
*/
public class BridgeTest {
public static void main(String[] args) {
Abstraction a = new RefinedAbstractionA(new ConcreteA());
a.operate();
Abstraction b = new RefinedAbstractionB(new ConcreteB());
b.operate();
}
}
//抽象实现
interface IImplementor{
void operate();
}
class ConcreteA implements IImplementor{
@Override
public void operate() {
System.out.println("a实现");
}
}
class ConcreteB implements IImplementor{
@Override
public void operate() {
System.out.println("b实现");
}
}
class ConcreteC implements IImplementor{
@Override
public void operate() {
System.out.println("c实现");
}
}
abstract class Abstraction{
private IImplementor iImplementor;
public Abstraction(IImplementor implementor) {
this.iImplementor = implementor;
}
public void operate() {
this.iImplementor.operate();
}
}
class RefinedAbstractionA extends Abstraction{
public RefinedAbstractionA(IImplementor implementor) {
super(implementor);
}
@Override
public void operate() {
super.operate();
System.out.println("a修正实现");
}
}
class RefinedAbstractionB extends Abstraction{
public RefinedAbstractionB(IImplementor implementor) {
super(implementor);
}
@Override
public void operate() {
super.operate();
System.out.println("a修正实现");
}
}
17.迭代器模式
是什么?Iterator Pattern又叫游标模式,提供一种按照顺序访问集合容器对象元素得方法,而无需暴露集合内部表示。可以为不同得容器提供一致得遍历行为,而不用关心内部元素得组成结构。属于行为型设计模式。
本质:把集合对象得迭代行为抽离到迭代器中,提供统一得接口。
场景:传送带
角色:抽象迭代器
具体迭代器
抽象容器
具体容器
源码实现:Iterator接口。
优点:多态迭代
简化集合对象接口
解耦迭代和集合
缺点:比较简单得遍历,使用迭代器模式比较繁琐
代码实现:
/**
* 迭代器测试
* 是什么?把集合对象得迭代行为抽离到迭代器中,提供一致得接口
* 角色:抽象迭代器
* 具体迭代器
* 抽象容器
* 具体容器
*/
public class IteratorTest {
public static void main(String[] args) {
//容器中添加元素
IAggregate<String> iAggregate = new ConcreteAggregate<>();
iAggregate.add("test");
iAggregate.add("test1");
iAggregate.add("test2");
//获取容器中得迭代器对象
Iterator<String> iterator =iAggregate.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
interface Iterator<E>{
boolean hasNext();
E next();
}
class ConcreteIterator<E> implements Iterator {
//集合
private List<E> list;
//游标
private int cursor = 0;
public ConcreteIterator(List<E> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return this.cursor<this.list.size();
}
@Override
public E next() {
return this.list.get(cursor++);
}
}
interface IAggregate<E> {
boolean add(E e);
boolean remove(E e);
Iterator<E> iterator();
}
class ConcreteAggregate<E> implements IAggregate<E> {
private List<E> list = new ArrayList<>();
@Override
public boolean add(E o) {
return this.list.add(o);
}
@Override
public boolean remove(E o) {
return this.list.remove(o);
}
@Override
public Iterator iterator() {
return new ConcreteIterator(this.list);
}
}
18.命令模式
是什么?Command Pattern模式,是对命令得封装,每一个命令都是一个操作:请求方发出请求要求执行一个操作;接收方接收到请求,并执行请求。命令模式解耦了请求方和接收方,请求方只管执行命令,不用关心命令如何被接收,怎么执行。属于行为型设计模式。
本质:解耦命令请求和处理。
角色:接收者角色
命令角色
具体命令角色
请求者角色。
总结:命令模式在实际生产中经常使用,比如我们的对外接口相当于请求者,在接口的实现上我们会抽零处理命令角色,由命令角色进行接收者的处理。
源码表现:Runable接口就是命令模式
优点:通过引入中间件,解耦命令请求与实现
扩展性比较好
支持组合命令
缺点:具体命令类会比较多
代码实现:
/**
* 命令模式测试:
* 是什么:解耦请求和处理
* 角色:接收者
* 请求者
* 命令角色
* 具体命令角色
*/
public class CommandTest {
public static void main(String[] args) {
Request request = new Request(new ConcreteCommand());
request.action();
}
}
//接收者
class Receiver{
public void action() {
System.out.println("执行具体操作1");
System.out.println("执行具体操作2");
System.out.println("执行具体操作3");
}
}
//命令者
interface ICommand{
void execute();
}
//具体命令者
class ConcreteCommand implements ICommand {
private Receiver receiver = new Receiver();
@Override
public void execute() {
receiver.action();
}
}
//请求者
class Request{
private ICommand iCommand;
public Request(ICommand command) {
this.iCommand = command;
}
public void action() {
this.iCommand.execute();
}
}
19.备忘录模式
是什么?Memento Pattern又叫快照模式(Snapshot Pattern)或者令牌模式(Token Pattern)
指在不破坏封装前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。属于行为型模式。
指的是后悔药模式。本质是:从发起人实体类隔离存储功能,降低实体类的职责。
场景:希望保存历史快照的场景
希望在对象之外保存状态,且出了自己,其他人不可以访问
角色:发起人角色 Originator 用于创建备忘录,回滚备忘录
备忘录角色 Memento 用于存储发起人状态,其他对象不可以访问
备忘录管理员角色 Caretaker 用于管理备忘录,没有操作和访问权限。
优点:简化发起人的职责,隔离状态存储与获取,实现信息的封装,客户端无须关心状态的保存细节。
提供状态回滚能力
缺点:消耗资源。保存状态太多,每一次保存都会消耗内存。
代码实现:
/**
* 备忘录模式测试
* 是什么?快照 行为型设计模式
* 角色:备忘录角色 用于存储备忘录,且可以防止发起人以外的对象访问
* 发起人角色 负责创建一个备忘录,记录自身保存的状态;具备状态回滚能力
* 备忘录管理员角色 负责存储、提供管理备忘录,无法对备忘录进行操作和访问。
*/
public class MementoTest {
public static void main(String[] args) {
//创建一个发起人角色
Originator originator = new Originator();
originator.setState("test");
//创建一个备忘录管理员角色
Caretaker caretaker = new Caretaker();
//管理员存储发起人的备忘录
caretaker.setMemento(originator.createMemento());
//发起人从管理员获取备忘录进行回滚
originator.restoreMemento(caretaker.getMemento());
}
}
//备忘录 用于存储发起人的状态,且可以防止除发起人其他对象访问
class Memento{
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
//发起人角色 创建备忘录;回滚备忘录
class Originator{
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento createMemento() {
return new Memento(this.state);
}
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
}
//备忘录管理员 用于存储提供备忘录,无法对备忘录内部进行操作和访问
class Caretaker{
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
20.中介模式
是什么?Mediator Pattern 又称为调停模式;用一个中介对象封装一系列的对象交互。中介者使各个对象不需要显示的相互调用,从而耦合松散。属于行为型设计模式
核心:包装一系列对象相互作用的方式。
角色:抽象中介者
具体中介者
抽象同事类
具体同事类
源码应用:Timer类
优点:减少类间依赖,将多对多转化成一对多,降低类间耦合;符合迪米特原则
缺点:中介膨胀
21工厂模式
几个工厂区别:
1.简单工厂模式 一个工厂生产不同产品;不属于23种设计模式中
2.工厂模式:多个工厂,每个工厂有自己的产品
3.抽象工厂模式:多个工厂,多个产品。
22.单例模式
是什么?Singleton Pattern 指的是一个类在任何情况下都是只有一个实例,并且只有一个访问点。
源码中:BeanFactory中实现单例方式是通过Map的方式,存在之间获取,不存在创建。
Runtime
优点:可以保证内存中只有一个实例存在,减少内存开销
可以避免对资源的多重占用
缺点:扩展困难,违背开闭原则。
不利于调试
代码:
/**
* 单例模式创建
*/
public class SingletonTest {
}
//饿汉单例 线程安全
class HungrySingleton{
private static HungrySingleton hungrySingleton = new HungrySingleton();
//构造方法私有化
private HungrySingleton(){}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
//饿汉单例 线程安全 使用静态代码块实现。会造成内存浪费
class HungrySingleton2{
private static HungrySingleton2 hungrySingleton;
//构造方法私有化
private HungrySingleton2(){
}
static{
hungrySingleton = new HungrySingleton2();
}
public static HungrySingleton2 getInstance() {
return hungrySingleton;
}
}
//为了避免内存浪费,采用懒加载方式
class LazySingleton{
private static LazySingleton lazySingleton;
private LazySingleton(){};
public static LazySingleton getInstance() {
//会存在线程安全问题
if(lazySingleton==null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
//解决线程安全问题
class LazySingleton2{
private static LazySingleton2 instance;
private LazySingleton2() {
}
//使用synchronized会造成大量线程阻塞,导致性能下降
public static synchronized LazySingleton2 getInstance() {
if(instance==null) {
instance = new LazySingleton2();
}
return instance;
}
}
//为了降低锁性能下降,使用DCL双重检查锁实现
class LazySingleton3{
private static LazySingleton3 instance;
private LazySingleton3(){};
public static LazySingleton3 getInstance() {
if(instance==null) {
synchronized (LazySingleton3.class) {
if(instance==null) {
instance = new LazySingleton3();
}
}
}
return instance;
}
}
//为了降低锁的开销,使用静态内部类方式;存在反射隐患
class LazySingleton4{
private static LazySingleton4 instance;
private LazySingleton4(){};
public static LazySingleton4 getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazySingleton4 INSTANCE = new LazySingleton4();
}
}
//为了解决内部类的反射隐患,进行优化,存在代码不够优雅的问题
class LazySingleton5{
private static LazySingleton5 instance;
private LazySingleton5(){
if(LazyHolder.INSTANCE!=null) {
throw new RuntimeException("不允许创建多个实例");
}
};
public static LazySingleton5 getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazySingleton5 INSTANCE = new LazySingleton5();
}
}
//为了解决代码优雅的问题,使用枚举的方式
enum LazySingleton6{
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static LazySingleton6 getInstance() {
return INSTANCE;
}
}
x.总结
设计模式名称 | 类型 | 场景 | 优缺点 |
责任链模式 | 行为型 | 请求和处理解耦;示例:各种校验场景 | 优点:请求和处理解耦 缺点:链式太多影响性能,依赖客户端 |
建造者模式 | 创建型 | 调用顺序多样,构建和表示分离。示例: | 优点:构建和表示分离,更注重方法的调用顺序; 构建和表示分离 缺点:产品变化,建造者跟着变化 |
代理模式 | 结构型 | 保护目标对象,增强目标对象。示例:日志处理,事务等 | 优点:代码解耦,代理对象和目标对象分离 缺点:增加一个代理对象,会让处理请求变慢。增加系统复杂度。 |
装饰器模式 | 结构型 | 功能扩展;示例:煎饼 | 优点:动态给一个功能做扩展,即插即用。遵循开闭原则。 缺点:会出现更多的类,代码,增加代码的复杂性; 动态装饰在多层装饰更复杂。 |
策略模式 | 行为型 | 不同的算法执行。示例:各种if-else | 优点:扩展性高, 缺点:策略类多了,不好维护 |
状态模式 | 行为型 | 状态变行为变。 | 优点:将状态转换显示化,具备扩展性 缺点:状态变化,代码就变,违背开闭原则 |
适配器模式 | 结构型 | 功能兼容问题。示例:电转换头 | 优点:提高类的复用,适配器类和原角色解耦 缺点:增加越多难度,降低代码可读性 |
观察者模式 | 行为型 | 应用层和数据层解耦。事件处理。示例:EventBus | 优点:观察者和被观察者松耦合,符合依赖倒置原则。分离了表示层和数据处理层。 缺点:观察者数量太多,性能有影响 |
模板模式 | 行为型 | 抽离公共到父类中,示例:JDBCTemplate,大象装冰箱 | 优点:提高代码复用性;提高代码扩展性 缺点:类爆炸,父类修改,子类跟着修改。 |
原型模式 | 创建型 | 对象复制,示例:Cloneable接口 | 优点:提升创建对象性能 缺点:每个类都得实现Cloneable接口,类变复杂 不符合开闭原则。 实现困难。极少使用。 |
门面模式 | 结构型 | 提高高层接口,完成子系统调用,示例:JDBCUtils | 优点:符合迪米特原则 减少系统依赖 简化调用过程,只调用一层高层接口接可 缺点:违反了开闭原则 子系统扩展容易产生风险 违反了单一职责原则 |
享元模式 | 结构性 | 减少对象创建 示例:Integer,Long,连接池 | 优点:减少对象创建,降低内存占用 缺点:是系统,逻辑复杂 |
组合模式 | 结构型 | 将单个对象和组合对象用相同得接口表示,使得客户对单个对象和组合对象使用具有一致性 | 优点:简化客户端代码,符合开闭原则,清楚定义各个层次得复杂对象,表示全部或部分层次。 缺点:设计比较抽象,限制类型时比较复杂。 |
桥接模式 | 结构型 | 2个维度得变化,分离抽象与部分实现,通过组合得形式完成。示例:Driver | 优点:扩展性好;分离抽象与部分具体实现。解决类爆炸问题 缺点:增加系统理解与设计难度 需要理清系统2个维度得变化 |
迭代器模式 | 行为型 | 集合元素遍历迭代,经常使用,不用自己去写。示例:Itertor | 优点:多态迭代,简化结合对象接口,解耦迭代和集合 缺点:简单遍历,使用迭代器比较繁琐 |
命令模式 | 行为型 | 解耦请求和处理,经常使用,需要抽离出来命令处理器。示例:对外接口的实现,会创建一个执行器 | 优点:解耦请求和处理。 扩展性比较好。 缺点:类膨胀 |
备忘录模式 | 行为型 | 快照机制,提供回滚能力 | 优点:隔离状态与获取,提供回滚能力 缺点:消耗内存 |
工厂模式 | 创建型 | 创建复杂对象。 | |
单例模式 | 创建型 | 保证只有一个实例 | 优点:减少内存开销,只维护一个实例 缺点:扩展苦难,违背开闭原则 |