java发邮件_初探Java设计模式3:⼀⽂带你掌握JDK中的设计
模式
⾏为型模式
⾏为型模式关注的是各个类之间的相互作⽤,将职责划分清楚,使得我们的代码更加地清晰。
策略模式
策略模式太常⽤了,所以把它放到最前⾯进⾏介绍。它⽐较简单,我就不废话,直接⽤代码说事吧。
下⾯设计的场景是,我们需要画⼀个图形,可选的策略就是⽤红⾊笔来画,还是绿⾊笔来画,或者蓝⾊笔来画。
⾸先,先定义⼀个策略接⼝:
public interface Strategy {  public void draw(int radius, int x, int y);}
然后我们定义具体的⼏个策略:
public class RedPen implements Strategy {  @Override  public void draw(int radius, int x, int y) {      System.out.println("⽤红⾊笔画图,radius:" + radius + ", x:" +使⽤策略的类:
public class Context {  private Strategy strategy;  public Context(Strategy strategy){      this.strategy = s
trategy;  }  public int executeDraw(int radius, int x
客户端演⽰:
public static void main(String[] args) {    Context context = new Context(new BluePen()); // 使⽤绿⾊笔来画      uteDraw(10, 0, 0);}
放到⼀张图上,让⼤家看得清晰些:
转存失败重新上传取消
这个时候,⼤家有没有联想到结构型模式中的桥梁模式,它们其实⾮常相似,我把桥梁模式的图拿过来⼤家对⽐下:
要我说的话,它们⾮常相似,桥梁模式在左侧加了⼀层抽象⽽已。桥梁模式的耦合更低,结构更复杂⼀些。
观察者模式
观察者模式对于我们来说,真是再简单不过了。⽆外乎两个操作,观察者订阅⾃⼰关⼼的主题和主题有数据变化后通知观察者们。
⾸先,需要定义主题,每个主题需要持有观察者列表的引⽤,⽤于在数据变更的时候通知各个观察者:
public class Subject {  private List observers = new ArrayList();  private int state;  public int getState() {      return state;  }  public void setState(int state) {      this 定义观察者接⼝:
public abstract class Observer {  protected Subject subject;  public abstract void update();}
其实如果只有⼀个观察者类的话,接⼝都不⽤定义了,不过,通常场景下,既然⽤到了观察者模式,我们就是希望⼀个事件出来了,会有多
个不同的类需要处理相应的信息。⽐如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到
通知等。
我们来定义具体的⼏个观察者类:
public class BinaryObserver extends Observer {      // 在构造⽅法中进⾏订阅主题    public BinaryObserver(Subject subject) {        this.subject = subject;        // 通常客户端使⽤也⾮常简单:
public static void main(String[] args) {    // 先定义⼀个主题      Subject subject1 = new Subject();      // 定义观察者      new BinaryObserver(subject1);      new HexaO output:
订阅的数据发⽣变化,新的数据处理为⼆进制值为:1011订阅的数据发⽣变化,新的数据处理为⼗六进制值为:B
当然,jdk 也提供了相似的⽀持,具体的⼤家可以参考 java.util.Observable 和 java.util.Observer 这两个类。
实际⽣产过程中,观察者模式往往⽤消息中间件来实现,如果要实现单机观察者模式,笔者建议读者使⽤ Guava 中的 EventBus,它有同
步实现也有异步实现,本⽂主要介绍设计模式,就不展开说了。
责任链模式
责任链通常需要先建⽴⼀个单向链表,然后调⽤⽅只需要调⽤头部节点就可以了,后⾯会⾃动流转下去。⽐如流程审批就是⼀个很好的例
⼦,只要终端⽤户提交申请,根据申请的内容信息,⾃动建⽴⼀条责任链,然后就可以开始流转了。
有这么⼀个场景,⽤户参加⼀个活动可以领取奖品,但是活动需要进⾏很多的规则校验然后才能放⾏,⽐如⾸先需要校验⽤户是否是新⽤
户、今⽇参与⼈数是否有限额、全场参与⼈数是否有限额等等。设定的规则都通过后,才能让⽤户领⾛奖品。
如果产品给你这个需求的话,我想⼤部分⼈⼀开始肯定想的就是,⽤⼀个 List 来存放所有的规则,然后 foreach 执⾏⼀下每个规则
就好了。不过,读者也先别急,看看责任链模式和我们说的这个有什么不⼀样?
⾸先,我们要定义流程上节点的基类:
public abstract class RuleHandler {      // 后继节点    protected RuleHandler successor;    public abstract void apply(Context context);    public void setSuccessor(R
接下来,我们需要定义具体的每个节点了。
校验⽤户是否是新⽤户:
public class NewUserRuleHandler extends RuleHandler {    public void apply(Context context) {        if (context.isNewUser()) {              // 如果有后继节点的话,传递
校验⽤户所在地区是否可以参与:
public class LocationRuleHandler extends RuleHandler {    public void apply(Context context) {        boolean allowed = activityService.isSupportedLocation(
校验奖品是否已领完:
public class LimitRuleHandler extends RuleHandler {    public void apply(Context context) {          int remainedTimes = activityService.queryRemainedTime
客户端:
public static void main(String[] args) {    RuleHandler newUserHandler = new NewUserRuleHandler();      RuleHandler locationHandler = new LocationRule
代码其实很简单,就是先定义好⼀个链表,然后在通过任意⼀节点后,如果此节点有后继节点,那么传递下去。
⾄于它和我们前⾯说的⽤⼀个 List 存放需要执⾏的规则的做法有什么异同,留给读者⾃⼰琢磨吧。
模板⽅法模式
在含有继承结构的代码中,模板⽅法模式是⾮常常⽤的,这也是在开源代码中⼤量被使⽤的。
通常会有⼀个抽象类:
public abstract class AbstractTemplate {    // 这就是模板⽅法      public void templateMethod(){        init();        apply(); // 这个是重点        end(); // 可以作为钩⼦⽅法
模板⽅法中调⽤了 3 个⽅法,其中 apply() 是抽象⽅法,⼦类必须实现它,其实模板⽅法中有⼏个抽象⽅法完全是⾃由的,我们也可以将
三个⽅法都设置为抽象⽅法,让⼦类来实现。也就是说,模板⽅法只负责定义第⼀步应该要做什么,第⼆步应该做什么,第三步应该做什
么,⾄于怎么做,由⼦类来实现。
我们写⼀个实现类:
public class ConcreteTemplate extends AbstractTemplate {    public void apply() {        System.out.println("⼦类实现抽象⽅法 apply");    }      public void end() {      客户端调⽤演⽰:
public static void main(String[] args) {    AbstractTemplate t = new ConcreteTemplate();      // 调⽤模板⽅法      t.templateMethod();}
代码其实很简单,基本上看到就懂了,关键是要学会⽤到⾃⼰的代码中。
状态模式
废话我就不说了,我们说⼀个简单的例⼦。商品库存中⼼有个最基本的需求是减库存和补库存,我们看看怎么⽤状态模式来写。
核⼼在于,我们的关注点不再是 Context 是该进⾏哪种操作,⽽是关注在这个 Context 会有哪些操作。
定义状态接⼝:
public interface State {  public void doAction(Context context);}
定义减库存的状态:
public class DeductState implements State {  public void doAction(Context context) {      System.out.println("商品卖出,准备减库存");      context.setState(this);
定义补库存状态:
public class RevertState implements State {    public void doAction(Context context) {        System.out.println("给此商品补库存");          context.setState(this);        前⾯⽤到了 context.setState(this),我们来看看怎么定义 Context 类:
public class Context {    private State state;      private String name;      public Context(String name) {        this.name = name;    }      public void setState(State state 我们来看下客户端调⽤,⼤家就⼀清⼆楚了:
public static void main(String[] args) {    // 我们需要操作的是 iPhone X    Context context = new Context("iPhone X");    // 看看怎么进⾏补库存操作      State revertSt
读者可能会发现,在上⾯这个例⼦中,如果我们不关⼼当前 context 处于什么状态,那么 Context 就可以不⽤维护 state 属性了,那样代
码会简单很多。
不过,商品库存这个例⼦毕竟只是个例,我们还有很多实例是需要知道当前 context 处于什么状态的。
⾏为型模式总结
⾏为型模式部分介绍了策略模式、观察者模式、责任链模式、模板⽅法模式和状态模式,其实,经典的⾏为型模式还包括备忘录模式、命令
模式等,但是它们的使⽤场景⽐较有限,⽽且本⽂篇幅也挺⼤了,我就不进⾏介绍了。
总结
学习设计模式的⽬的是为了让我们的代码更加的优雅、易维护、易扩展。这次整理这篇⽂章,让我重新审视了⼀下各个设计模式,对我⾃⼰
⽽⾔收获还是挺⼤的。我想,⽂章的最⼤收益者⼀般都是作者本⼈,为了写⼀篇⽂章,需要巩固⾃⼰的知识,需要寻各种资料,⽽且,⾃
⼰写过的才最容易记住,也算是我给读者的建议吧。
(全⽂完)
本系列⽂章将整理到我在GitHub上的《Java⾯试指南》仓库,更多精彩内容请到我的仓库⾥查看
喜欢的话⿇烦点下Star、fork哈
⽂章也将发表在我的个⼈博客,阅读体验更佳:
www.how2playlife
java中常用的设计模式有哪些
结构型模式
前⾯创建型模式介绍了创建对象的⼀些设计模式,这节介绍的结构型模式旨在通过改变代码结构来达到解耦的⽬的,使得我们的代码容易维
护和扩展。
代理模式
第⼀个要介绍的代理模式是最常使⽤的模式之⼀了,⽤⼀个代理来隐藏具体实现类的实现细节,通常还⽤于在真实的实现的前后添加⼀部分
逻辑。
既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,⽽
是⼀层⽪⽽已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。
理解代理这个词,这个模式其实就简单了。
public interface FoodService {    Food makeChicken();    Food makeNoodle();}public class FoodServiceImpl implements FoodService {    public Food makeChicke
客户端调⽤,注意,我们要⽤代理来实例化接⼝:
// 这⾥⽤代理类来实例化FoodService foodService = new FoodServiceProxy();foodService.makeChicken();
我们发现没有,代理模式说⽩了就是做 “⽅法包装” 或做 “⽅法增强”。在⾯向切⾯编程中,算了还是不要吹捧这个名词了,在 AOP
中,其实就是动态代理的过程。⽐如 Spring 中,我们⾃⼰不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在
@Before、@After、@Around 中的代码逻辑动态添加到代理中。
说到动态代理,⼜可以展开说 …… Spring 中实现动态代理有两种,⼀种是如果我们的类定义了接⼝,如 UserService 接⼝和
UserServiceImpl 实现,那么采⽤ JDK 的动态代理,感兴趣的读者可以去看看 flect.Proxy 类的源码;另⼀种是我们⾃⼰没
有定义接⼝的,Spring 会采⽤ CGLIB 进⾏动态代理,它是⼀个 jar 包,性能还不错。
适配器模式
说完代理模式,说适配器模式,是因为它们很相似,这⾥可以做个⽐较。
适配器模式做的就是,有⼀个接⼝需要实现,但是我们现成的对象都不满⾜,需要加⼀层适配器来进⾏适配。
适配器模式总体来说分三种:默认适配器模式、对象适配器模式、类适配器模式。先不急着分清楚这⼏个,先看看例⼦再说。
默认适配器模式
⾸先,我们先看看最简单的适配器模式默认适配器模式(Default Adapter)是怎么样的。
我们⽤ Appache commons-io 包中的 FileAlterationListener 做例⼦,此接⼝定义了很多的⽅法,⽤于对⽂件或⽂件夹进⾏监控,⼀旦
发⽣了对应的操作,就会触发相应的⽅法。
public interface FileAlterationListener {    void onStart(final FileAlterationObserver observer);    void onDirectoryCreate(final File directory);    void onDirectoryChan
此接⼝的⼀⼤问题是抽象⽅法太多了,如果我们要⽤这个接⼝,意味着我们要实现每⼀个抽象⽅法,如果我们只是想要监控⽂件夹中的⽂件
创建和⽂件删除事件,可是我们还是不得不实现所有的⽅法,很明显,这不是我们想要的。
所以,我们需要下⾯的⼀个适配器,它⽤于实现上⾯的接⼝,但是所有的⽅法都是空⽅法,这样,我们就可以转⽽定义⾃⼰的类来继承下⾯
这个类即可。
public class FileAlterationListenerAdaptor implements FileAlterationListener {    public void onStart(final FileAlterationObserver observer) {    }    public void onDire ⽐如我们可以定义以下类,我们仅仅需要实现我们想实现的⽅法就可以了:
public class FileMonitor extends FileAlterationListenerAdaptor {    public void onFileCreate(final File file) {        // ⽂件创建        doSomething();    }    public void onF 当然,上⾯说的只是适配器模式的其中⼀种,也是最简单的⼀种,⽆需多⾔。下⾯,再介绍“正统的”适配器模式。
对象适配器模式