写点什么

架构第三周 - 学习总结

用户头像
J.Spring
关注
发布于: 2020 年 06 月 23 日
架构第三周-学习总结

这周主要的学习内容就是关于设计模式、设计原则、重构

心得体会

除了课堂内的内容,还有些比较重要的题外话我想先拿出来讲一下。

  • 找出问题的人比解决问题的人高出一个Level。好比发现设计问题比去用这些设计模式更NB一点。

  • 阅读源码最好是好奇心驱动、工作驱动。

其实我在找工作的之前或者过程中做了这部分补充,比如我阅读了spring的源码,串联了知识体系,理解了IOC、AOP的设计源码和原理。源码之下无秘密,所言不虚!

  • 忽略不必要的细节以节省精力专注于更加重要的事情上。当然这个也是因人而异。



SOLID设计原则

  • 单一职责原则(S)

  • 开闭原则(O)

  • 里式替换原则(L):与多态思想不同,是一种指导子类设计的设计原则,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。

  • 接口隔离原则(I):客户端不应该被强迫依赖它不需要的接口。

  • 依赖倒置原则(D):高层模块不要依赖底层模块,抽象不要依赖具体。

其他还有:KISS原则(Keep It Simple and Stupid) DRY原则、YAGNI原则、迪米特法则(有限知识原则、不要和陌生人说话)



设计模式

策略模式

策略的定义

策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。

因为所有的策略类都实现相同的接口。所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策略!



策略的创建

因为策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。

为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。我们可以把根据 type 创建策略的逻辑抽离出来,放到工厂类中



本质上都是借助“查表法”,根据 type 查表(代码中的 strategies 就是表)替代根据 type 分支判断。

无状态策略

这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,都创建一个新的策略对象。



这个工厂代码存放的一般是无状态策略



有状态策略

如果策略类是有状态的,根据业务场景的需要,我们希望每次从工厂方法中,获得的都是新创建的策略对象,而不是缓存好可共享的策略对象,那我们就需要按照如下方式来实现策略工厂类!





单例模式的几种方式

饿汉式

在类加载的时候静态实例就已经初始化好了,线程安全。





懒汉式

延迟加载初始化,一把大锁,线程安全。





双重检测





原型模式

原型模式:其实就是从一个对象再创建另外一个可定制的对象,而不需要知道任何的创建细节。

  • 分为浅层复制和深层复制。

  • java的clone()方法默认为浅层复制。引用类型只会复制指针但是不会复制数据。

  • 深层复制的实现方式:



工厂模式

简单工厂模式+工厂方法模式

简单工厂模式

直接在条件判断中根据不同参数将目标对象new了出来。





工厂方法模式

是将目标对象的创建过程根据参数分别抽取到各自独立的工厂类中,以应对目标对象创建过程的复杂度。条件分支可以使用map来缓存起来!







案例:java.util.Calendar的getInstance() 方法可以根据不同 TimeZone 和 Locale,创建不同的 Calendar 子类对象。也是用到建造者模式。

建造者模式

通过内部的Builder类主导目标对象的创建同时校验必选属性之间的依赖关系。如下set方法中包含一些校验,然后再build()中统一校验。



new ConstructorArg.Builder()
.setIsRef(false).setType(String.class).setObject(2)
.build()




装饰器模式

装饰器模式把每个要装饰的功能放到单独的类中,并让这个类包装它所需要装饰的对象,从而实现对原始类的增强。

代理模式与装饰器模式的区别:

  • 装饰器模式中,装饰器类附加的是跟原始类相关的增强功能

  • 代理模式中,代理类附加的是跟原始类无关的功能

案例:java的IO类库比如InputStream和BufferInputStream。还有Collections类。





职责链模式

在职责链模式中,多个处理器依次处理同一个请求。

一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作职责链模式



在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求(下面代码所示)。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。



职责链模式有两种常用的实现。一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

应用场景:Servlet中的Filter、Netty的ChannelPipeline.



public interface IHandler {
boolean handle();
}
public class HandlerA implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerB implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle() {
for (IHandler handler : handlers) {
boolean handled = handler.handle();
if (handled) {
break;
}
}
}
}
// 使用举例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}



代理模式

  • 静态代理

  • jdk动态代理:被代理类需要实现接口





  • cglib动态代理:不需要实现接口

  • 如果被代理类被标记成final,也就无法通过CGLIB去创建动态代理:

java.lang.IllegalArgumentException: Cannot subclass final class class spring.PersonServiceImpl



  • 如果被代理方法被标记为final,也无法正常实现代理功能





SpringAOP的实现:

  • 如果目标对象实现了接口,默认采用JDK动态代理。

  • 如果没有实现接口则采用CGLIB动态代理。



  • 模板方法模式

由父类实现通用逻辑,具体细节留给子类去实现。减少代码重复度!





适配器模式

  • 这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

  • 适配器模式有两种实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现



代码实现:

其中,ITarget 表示要转化成的接口定义。Adaptee 是一组不兼容 ITarget 接口定义的接口,Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口

类适配器: 基于继承



public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() { //... }
public void fb() { //... }
public void fc() { //... }
}
public class Adaptor extends Adaptee implements ITarget {
public void f1() {
super.fa();
}
public void f2() {
//...重新实现f2()...
}
// 这里fc()不需要实现,直接继承自Adaptee,这是跟对象适配器最大的不同点
}



对象适配器:基于组合



public interface ITarget {
void f1();
void f2();
void fc();
}
public class Adaptee {
public void fa() { //... }
public void fb() { //... }
public void fc() { //... }
}
public class Adaptor implements ITarget {
private Adaptee adaptee;
public Adaptor(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void f1() {
adaptee.fa(); //委托给Adaptee
}
public void f2() {
//...重新实现f2()...
}
public void fc() {
adaptee.fc();
}
}



如何选择?

  • 如果 Adaptee 接口并不多,那两种实现方式都可以!

  • 如果 Adaptee 接口很多呢?

  • Adaptee 和 ITarget 接口定义大部分都相同,推荐使用类适配器,因为可以让 Adaptor 复用父类 Adaptee 的接口。

  • Adaptee 和 ITarget 接口定义大部分都不相同,推荐使用对象适配器,因为组合结构相对于继承更加灵活!



  • SpringMVC中适配器模式



针对Servlet、Controller、注解的Handler组件适配为HandlerAdapter,统一调用handle(...)方法进行逻辑处理,省去了handler具体实现类的判断



// 之前的实现方式
Handler handler = handlerMapping.get(URL);
if (handler instanceof Controller) {
((Controller)handler).handleRequest(...);
} else if (handler instanceof Servlet) {
((Servlet)handler).service(...);
} else if (hanlder 对应通过注解来定义的Controller) {
反射调用方法...
}
// 现在实现方式
HandlerAdapter handlerAdapter = handlerMapping.get(URL);
handlerAdapter.handle(...);



发布于: 2020 年 06 月 23 日阅读数: 49
用户头像

J.Spring

关注

努力支撑经历,经历支撑能力! 2018.12.03 加入

追不上BAT的人... 分享,聚焦

评论

发布
暂无评论
架构第三周-学习总结