diff --git a/README.md b/README.md index fb1ed7f..2083ef1 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,18 @@ # 设计模式简介 -* 1 [单例模式](https://github.com/landy8530/DesignPatterns/wiki/1.-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F) -* 2 [责任链模式](https://github.com/landy8530/DesignPatterns/wiki/2.-%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F) -* 3 [策略模式](https://github.com/landy8530/DesignPatterns/wiki/3.-%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F) -* 4 [模板方法模式](https://github.com/landy8530/DesignPatterns/wiki/4.-%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F) -* 5 [工厂方法模式](https://github.com/landy8530/DesignPatterns/wiki/5.-%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F) -* 6 [抽象工厂模式](https://github.com/landy8530/DesignPatterns/wiki/6.-%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F) -* 7 [建造者模式](https://github.com/landy8530/DesignPatterns/wiki/7.-%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F) -* 8 [代理模式](https://github.com/landy8530/DesignPatterns/wiki/8.-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F) -* 9 [装饰模式](https://github.com/landy8530/DesignPatterns/wiki/9.-%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F) -* 10 [原型模式](https://github.com/landy8530/DesignPatterns/wiki/10.-%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F) -* 11 [委派模式](https://github.com/landy8530/DesignPatterns/wiki/11.-%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%BC%8F) +* [1 单例模式](https://github.com/landy8530/DesignPatterns/wiki/1.-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F) +* [2 责任链模式](https://github.com/landy8530/DesignPatterns/wiki/2.-%E8%B4%A3%E4%BB%BB%E9%93%BE%E6%A8%A1%E5%BC%8F) +* [3 策略模式](https://github.com/landy8530/DesignPatterns/wiki/3.-%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F) +* [4 模板方法模式](https://github.com/landy8530/DesignPatterns/wiki/4.-%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F) +* [5 工厂方法模式](https://github.com/landy8530/DesignPatterns/wiki/5.-%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F) +* [6 抽象工厂模式](https://github.com/landy8530/DesignPatterns/wiki/6.-%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F) +* [7 建造者模式](https://github.com/landy8530/DesignPatterns/wiki/7.-%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F) +* [8 代理模式](https://github.com/landy8530/DesignPatterns/wiki/8.-%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F) +* [9 装饰模式](https://github.com/landy8530/DesignPatterns/wiki/9.-%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F) +* [10 原型模式](https://github.com/landy8530/DesignPatterns/wiki/10.-%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F) +* [11 委派模式](https://github.com/landy8530/DesignPatterns/wiki/11.-%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%BC%8F) +* [12 适配器模式](https://github.com/landy8530/DesignPatterns/wiki/12-%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F) # 设计模式综合运用 diff --git a/README_BAKUP.md b/README_BAKUP.md index 37dca1c..17826cd 100644 --- a/README_BAKUP.md +++ b/README_BAKUP.md @@ -637,6 +637,119 @@ Spring 默认是采用单例模式 在Spring中,以Delegate结尾的类,以Dispatcher结尾的类基本上都是委派模式实现。 +## 12 适配器模式 + +代码参见 `design-patterns-basic` 工程中的adapter包。 + +### 12.1 概念 + +适配器模式是一种适配中间件的模式,适配接口(类)和被适配接口(类)之间是没有层次关系的。 + +### 12.2 实现方式 + +适配器模式目前有三种实现方式,类适配器模式、对象适配器模式、接口适配器模式。 + +#### 12.2.1 类适配器模式 + +通过继承来实现类适配器功能,当我们要访问的接口A中没有想要的方法,而在另一个接口B中有此方法,但是又不能改变访问接口A,这时候我们就可以定义一个适配器来适配两个接口。这个适配器要实现我们的接口A,然后继承接口B的实现类即可。 + +```java +public class PS2ToUsberAdapter extends Usber implements PS2 { + @Override + public boolean isPs2() { + System.out.println("PS2接口转化为了USB接口"); + return isUsb(); + } +} +``` + +#### 12.2.2 对象适配器模式 + +通过组合来实现适配器功能,当我们要访问的接口A中没有想要的方法,而在另一个接口B中有此方法,但是又不能改变访问接口A,这时候我们就可以定义一个适配器来适配两个接口。这个适配器要实现我们的接口A,而且要引入成员变量接口B的实例,再定义一个带参数的构造方法(参数类型就是接口B),再在接口A中的方法中调用成员变量对象的方法。 + +```java +public class PS2ToUsberAdapter implements PS2 { + + private USB usb; + + public PS2ToUsberAdapter(USB usb) { + this.usb = usb; + } + + @Override + public boolean isPs2() { + System.out.println("PS2接口转化为了USB接口"); + return usb.isUsb(); + } +} +``` + +#### 12.2.3 接口适配器模式 + +通过抽象类来实现适配器功能,当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。 + +```java +public abstract class MouseAdapter implements MouseListener, MouseWheelListener, MouseMotionListener { + /** + * {@inheritDoc} + */ + public void mouseClicked(MouseEvent e) {} + + /** + * {@inheritDoc} + */ + public void mousePressed(MouseEvent e) {} + + /** + * {@inheritDoc} + */ + public void mouseReleased(MouseEvent e) {} + + /** + * {@inheritDoc} + */ + public void mouseEntered(MouseEvent e) {} + + /** + * {@inheritDoc} + */ + public void mouseExited(MouseEvent e) {} + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void mouseWheelMoved(MouseWheelEvent e){} + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void mouseDragged(MouseEvent e){} + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void mouseMoved(MouseEvent e){} +} +``` + +### 12.3 适用场景 + +#### 12.3.1 类适配器与对象适配器模式 + +类适配器与对象适配器的使用场景一致,仅仅是实现手段稍有区别,二者主要用于如下场景: + +- 想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。 +- 一个类想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。 + +以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。 + +#### 12.3.2 接口适配器模式 + +想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。 + # 设计模式综合运用 ## 1. 门面+模版方法+责任链+策略 @@ -1494,4 +1607,56 @@ target class:org.landy.business.rules.stategy.CustomerUpdateRule@ae3540e org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.landy.business.rules.CustomerUpdateRuleTest': Unsatisfied dependency expressed through field 'customerUpdateRule'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'customerUpdateRule' is expected to be of type 'org.landy.business.rules.stategy.CustomerUpdateRule' but was actually of type 'com.sun.proxy.$Proxy34' ``` -与生成的代理类型不一致,有兴趣的同学可以Debug `DefaultAopProxyFactory`类中的`createAopProxy`方法即可自动两种动态代理的区别。 \ No newline at end of file +与生成的代理类型不一致,有兴趣的同学可以Debug `DefaultAopProxyFactory`类中的`createAopProxy`方法即可自动两种动态代理的区别。 + +## 4 责任链模式进阶 + +### 4.1 责任链模式现存缺点 + + + +### 4.2 改进方式 + +#### 4.2.1 引入适配器模式 + +关于接口适配器模式原理以及使用场景请参见wiki [12 适配器模式](https://github.com/landy8530/DesignPatterns/wiki/12-%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F) 。 + +#### 4.2.2 引入接口默认方法 + +> 事例代码请参见工程 `design-patterns-business`中的 `defaultmethod`包下的代码。 + +##### 4.2.2.1 概念 + +- java8引入了一个 `default medthod ` +- 使用 `default `关键字 +- Spring 4.2支持加载在默认方法里声明的bean + +##### 4.2.2.2 优点 + +- 用来扩展已有的接口,在对已有接口的使用不产生任何影响的情况下,添加扩展。 + + > 比如我们已经投入使用的接口需要拓展一个新的方法,在Java8以前,如果为一个使用的接口增加一个新方法,则我们必须在所有实现类中添加该方法的实现,否则编译会出现异常。如果实现类数量少并且我们有权限修改,可能会工作量相对较少。如果实现类比较多或者我们没有权限修改实现类源代码,这样可能就比较麻烦。而默认方法则解决了这个问题,它提供了一个实现,当没有显示提供其他实现时就采用这个实现,这样新添加的方法将不会破坏现有代码。 + +- 默认方法的另一个优势是该方法是可选的,子类可以根据不同的需求Override默认实现。 + + > 例如,我们定义一个集合接口,其中有增、删、改等操作。如果我们的实现类90%都是以数组保存数据,那么我们可以定义针对这些方法给出默认实现,而对于其他非数组集合或者有其他类似业务,可以选择性复写接口中默认方法。 + +##### 4.2.2.3 使用原则 + +- ”类优先” 原则 + - 若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时选择父类中的方法:如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。 +- 接口冲突原则 + - 如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。 + +### 4.3 项目演示 + +#### 4.3.1 逻辑梳理 + +由于系统业务需求的变更,目前有两种业务需求, + +1. 一种就是按照原来的文件上传需求,上传过程中需要校验操作,只要校验失败了,就直接返回失败信息,整个文件都不需要做处理了。(参见第一节内容 [1 门面+模版方法+责任链+策略](https://github.com/landy8530/DesignPatterns/wiki/1.-%E9%97%A8%E9%9D%A2-%E6%A8%A1%E7%89%88%E6%96%B9%E6%B3%95-%E8%B4%A3%E4%BB%BB%E9%93%BE-%E7%AD%96%E7%95%A5)) +2. 另一种就是校验的逻辑都一样,但是如果校验失败的情况,需要继续往下执行,记录一下失败id即可,即需要把失败的记录也保存到数据库中,比如约束字段不符合约束规则的情况,则把此字段置空,然后继续执行其他校验器逻辑即可。(本节需要讨论的问题) + +#### 4.3.2 实现细节 + +#### 4.3.3 逻辑测试 \ No newline at end of file diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/PS2ToUsberAdapter.java b/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/PS2ToUsberAdapter.java new file mode 100644 index 0000000..c5c2cc9 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/PS2ToUsberAdapter.java @@ -0,0 +1,17 @@ +package org.landy.adapter.classadapter; + +import org.landy.adapter.domain.PS2; +import org.landy.adapter.domain.Usber; + +/** + * @author: landy + * @date: 2019/5/19 11:37 + * @description: + */ +public class PS2ToUsberAdapter extends Usber implements PS2 { + @Override + public boolean isPs2() { + System.out.println("PS2接口转化为了USB接口"); + return isUsb(); + } +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/Test.java b/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/Test.java new file mode 100644 index 0000000..6b3cfa7 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/classadapter/Test.java @@ -0,0 +1,17 @@ +package org.landy.adapter.classadapter; + +import org.landy.adapter.domain.PS2; + +/** + * @author: landy + * @date: 2019/5/19 11:39 + * @description: + */ +public class Test { + + public static void main(String[] args) { + PS2 p = new PS2ToUsberAdapter(); + p.isPs2(); + } + +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/domain/PS2.java b/design-patterns-basic/src/main/java/org/landy/adapter/domain/PS2.java new file mode 100644 index 0000000..3fbebb7 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/domain/PS2.java @@ -0,0 +1,12 @@ +package org.landy.adapter.domain; + +/** + * @author: landy + * @date: 2019/5/19 11:36 + * @description: + */ +public interface PS2 { + + boolean isPs2(); + +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/domain/USB.java b/design-patterns-basic/src/main/java/org/landy/adapter/domain/USB.java new file mode 100644 index 0000000..0282c20 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/domain/USB.java @@ -0,0 +1,12 @@ +package org.landy.adapter.domain; + +/** + * @author: landy + * @date: 2019/5/19 11:35 + * @description: + */ +public interface USB { + + boolean isUsb(); + +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/domain/Usber.java b/design-patterns-basic/src/main/java/org/landy/adapter/domain/Usber.java new file mode 100644 index 0000000..de9d976 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/domain/Usber.java @@ -0,0 +1,16 @@ +package org.landy.adapter.domain; + +import org.landy.adapter.domain.USB; + +/** + * @author: landy + * @date: 2019/5/19 11:36 + * @description: + */ +public class Usber implements USB { + @Override + public boolean isUsb() { + System.out.println("这是一个USB接口"); + return true; + } +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/interfaceadapter/Test.java b/design-patterns-basic/src/main/java/org/landy/adapter/interfaceadapter/Test.java new file mode 100644 index 0000000..f846231 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/interfaceadapter/Test.java @@ -0,0 +1,15 @@ +package org.landy.adapter.interfaceadapter; + +/** + * @author: landy + * @date: 2019/5/19 12:07 + * @description: + */ +public class Test { + + public static void main(String[] args) { + // 比如java.awt.event.MouseAdapter类就是一个接口适配器,他实现的都是空方法, + // 由具体的子类选择性的挑选需要的方法进行相应的实现 + } + +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/PS2ToUsberAdapter.java b/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/PS2ToUsberAdapter.java new file mode 100644 index 0000000..817d27b --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/PS2ToUsberAdapter.java @@ -0,0 +1,24 @@ +package org.landy.adapter.objectadapter; + +import org.landy.adapter.domain.PS2; +import org.landy.adapter.domain.USB; + +/** + * @author: landy + * @date: 2019/5/19 11:52 + * @description: + */ +public class PS2ToUsberAdapter implements PS2 { + + private USB usb; + + public PS2ToUsberAdapter(USB usb) { + this.usb = usb; + } + + @Override + public boolean isPs2() { + System.out.println("PS2接口转化为了USB接口"); + return usb.isUsb(); + } +} diff --git a/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/Test.java b/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/Test.java new file mode 100644 index 0000000..73a9446 --- /dev/null +++ b/design-patterns-basic/src/main/java/org/landy/adapter/objectadapter/Test.java @@ -0,0 +1,18 @@ +package org.landy.adapter.objectadapter; + +import org.landy.adapter.domain.PS2; +import org.landy.adapter.domain.Usber; + +/** + * @author: landy + * @date: 2019/5/19 11:55 + * @description: + */ +public class Test { + + public static void main(String[] args) { + PS2 p = new PS2ToUsberAdapter(new Usber()); + p.isPs2(); + } + +} diff --git a/design-patterns-business/src/test/java/defaultmethod/Addition.java b/design-patterns-business/src/test/java/defaultmethod/Addition.java new file mode 100644 index 0000000..80b66ce --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/Addition.java @@ -0,0 +1,14 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:29 + * @description: + */ +public interface Addition { + + default int add(int a, int b) { + return a + b; + } + +} diff --git a/design-patterns-business/src/test/java/defaultmethod/AdditionImpl.java b/design-patterns-business/src/test/java/defaultmethod/AdditionImpl.java new file mode 100644 index 0000000..694b9fa --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/AdditionImpl.java @@ -0,0 +1,22 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:30 + * @description: + */ +public class AdditionImpl implements Addition { + + /** + * 接口默认方法的 ”类优先” 原则:若一个接口中定义了一个默认方法, + * 而另外一个父类或接口中又定义了一个同名的方法时选择父类中的方法: + * 如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。 + * @param a + * @param b + * @return + */ + @Override + public int add(int a, int b) { + return a + b + 10; + } +} diff --git a/design-patterns-business/src/test/java/defaultmethod/AdditionImpl2.java b/design-patterns-business/src/test/java/defaultmethod/AdditionImpl2.java new file mode 100644 index 0000000..600bbf7 --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/AdditionImpl2.java @@ -0,0 +1,9 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:33 + * @description: + */ +public class AdditionImpl2 implements Addition { +} diff --git a/design-patterns-business/src/test/java/defaultmethod/Additional.java b/design-patterns-business/src/test/java/defaultmethod/Additional.java new file mode 100644 index 0000000..1d252ea --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/Additional.java @@ -0,0 +1,12 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:34 + * @description: + */ +public interface Additional { + + int add(int a, int b); + +} diff --git a/design-patterns-business/src/test/java/defaultmethod/InterfaceConflict.java b/design-patterns-business/src/test/java/defaultmethod/InterfaceConflict.java new file mode 100644 index 0000000..ca3b722 --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/InterfaceConflict.java @@ -0,0 +1,22 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:35 + * @description: + */ +public class InterfaceConflict implements Addition, Additional { + + /** + * 接口冲突:如果一个父接口提供一个默认方法, + * 而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法), + * 那么必须覆盖该方法来解决冲突 + * @param a + * @param b + * @return + */ + @Override + public int add(int a, int b) { + return a + b + 5; + } +} diff --git a/design-patterns-business/src/test/java/defaultmethod/TestAdd.java b/design-patterns-business/src/test/java/defaultmethod/TestAdd.java new file mode 100644 index 0000000..6203522 --- /dev/null +++ b/design-patterns-business/src/test/java/defaultmethod/TestAdd.java @@ -0,0 +1,22 @@ +package defaultmethod; + +/** + * @author: landy + * @date: 2019/5/19 09:30 + * @description: + */ +public class TestAdd { + + public static void main(String[] args) { + Addition addition = new AdditionImpl(); + System.out.println(addition.add(10,10)); + + Addition addition1 = new AdditionImpl2(); + System.out.println(addition1.add(10,10)); + + InterfaceConflict ic = new InterfaceConflict(); + System.out.println(ic.add(10,10)); + + } + +}