设计模式
简单工厂
由一个工厂对象决定创建出哪一种产品类的实例。
创建型,但不属于 GOF23 种设计模式。
编码风格,不属于设计模式。
1# 适用场景
2* 工厂类负责创建的对象比较少
3* 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心
4
5# 优点
6* 只需要传入一个正确的参数,就可以获取你所需要的对象,无需知道其创建细节。
7
8# 缺点
9* 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。
10* 无法形成基于继承的结构。
Video
1public abstract class Video {
2 public abstract void produce();
3}
JavaVideo
1public class JavaVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("Java 开讲了!!!");
5 }
6}
PythonVideo
1public class PythonVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("Python开讲了!!!");
5 }
6}
VideoFactory
1public class VideoFactory {
2
3 /**
4 * 重载 通过反射弥补扩展性(复杂的逻辑判断)
5 * @param Class c
6 * @return Video
7 */
8 public Video getVideo(Class c){
9 Video video = null;
10 try {
11 video = (Video) Class.forName(c.getName()).newInstance();
12 } catch (InstantiationException e) {
13 e.printStackTrace();
14 } catch (IllegalAccessException e) {
15 e.printStackTrace();
16 } catch (ClassNotFoundException e) {
17 e.printStackTrace();
18 }
19 return video;
20 }
21
22 /**
23 * 重载 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。
24 * @param String type
25 * @return Video
26 */
27 public Video getVideo(String type){
28 if("java".equalsIgnoreCase(type)){
29 return new JavaVideo();
30 }else if("python".equalsIgnoreCase(type)){
31 return new PythonVideo();
32 }
33 return null;
34 }
35}
Test
1public class Test {
2 public static void main(String[] args) {
3 //创建工厂
4 VideoFactory videoFactory = new VideoFactory();
5 //工厂根据传入的参数生产出对应的对象
6 Video video = videoFactory.getVideo(JavaVideo.class);
7 //Video video = videoFactory.getVideo("java");
8 if(video == null){
9 return;
10 }
11 //指定目标逻辑
12 video.produce();
13 }
14}
案例: JDK 的 Calendar
1private static Calendar createCalendar(TimeZone zone,
2 Locale aLocale){... 根据不同国家做判断}
案例: JDBC 加载启动
1 Class.forName("com.mysql.jdbc.Driver");
2
3// Driver 类
4package com.mysql.jdbc;
5
6public class Driver extends NonRegisteringDriver implements java.sql.Driver {
7 public Driver() throws SQLException {
8 }
9 //使用静态代码块注册了驱动:这里注册的Driver 是mysql的Driver
10 static {
11 try {
12 DriverManager.registerDriver(new Driver());
13 } catch (SQLException var1) {
14 throw new RuntimeException("Can't register driver!");
15 }
16 }
17}
案例: logback 的 LoggerContext
1 public final Logger getLogger(final String name) {
2 Logger childLogger = (Logger) loggerCache.get(name);
3 }
工厂方法
定义一个创建对象的抽象方法,让子类通过继承来实现该抽象方法,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化过程推迟到子类中进行,也就是将创建对象的任务委托给不同的子类工厂
。
工厂方法是为了解决同一产品等级业务抽象问题。
创建型。
1# 适用场景
2* 创建对象需要大量重复的代码
3* 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
4* 一个类通过其子类来指定创建哪个对象
5
6# 优点
7* 用户只需要关心所需产品对应的工厂,无须关心创建细节
8* 加入新产品符合开闭原则,提高可扩展性
9
10# 缺点
11* 类的个数容易过多,增加复杂度。(不同类型的工厂类,负责生生产不同类型的产品类)
12* 增加了系统的抽象性和理解难度
Video
1public abstract class Video {
2 //Java、Python 都属于同一个产品等级
3 public abstract void produce();
4
5}
VideoFactory
1public abstract class VideoFactory {
2 public abstract Video getVideo();
3}
JavaVideo
1public class JavaVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("录制Java课程视频");
5 }
6}
JavaVideoFactory
1public class JavaVideoFactory extends VideoFactory {
2 @Override
3 public Video getVideo() {
4 return new JavaVideo();
5 }
6}
PythonVideo
1public class PythonVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("录制Python课程视频");
5 }
6}
PythonVideoFactory
1public class PythonVideoFactory extends VideoFactory {
2 @Override
3 public Video getVideo() {
4 return new PythonVideo();
5 }
6}
Test
1public class Test {
2 public static void main(String[] args) {
3 VideoFactory videoFactory = new PythonVideoFactory();
4 VideoFactory videoFactory2 = new JavaVideoFactory();
5 //通过工厂创建具体实例
6 Video video = videoFactory.getVideo();
7 video.produce();
8 }
9}
案例: Collection 类的 iterator() 方法
1抽象方法是 Collection 类的 iterator() 方法
2具体实现工厂 ArrayList 类
3具体创建的产品的方法是 ArrayList 类的 iterator()
4抽象产品是 Iterator<E> 接口
5实际产品 Collection 的各个具体实现类 iterator() 方法 返回的 Iterator<E>
抽象工厂
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口。
无须指定它们具体的类
创建型
1# 适用场景
2* 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
3* 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
4* 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
5
6# 优点
7* 具体产品在应用层代码隔离,无需关系创建细节
8* 将一个系列的产品族统一到一起创建
9
10# 缺点
11* 新增产品等级比较困难,规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
12* 增加了系统的抽象性和理解难度
CourseFactory
1public interface CourseFactory {
2 Video getVideo();
3 Article getArticle();
4}
Video
1public abstract class Video {
2 public abstract void produce();
3
4}
Article
1public abstract class Article {
2 public abstract void produce();
3}
JavaVideo
1public class JavaVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("录制Java课程视频");
5 }
6}
JavaArticle
1public class JavaArticle extends Article {
2 @Override
3 public void produce() {
4 System.out.println("编写Java课程手记");
5 }
6}
JavaCourseFactory
1public class JavaCourseFactory implements CourseFactory {
2 @Override
3 public Video getVideo() {
4 return new JavaVideo();
5 }
6
7 @Override
8 public Article getArticle() {
9 return new JavaArticle();
10 }
11}
PythonVideo
1public class PythonVideo extends Video {
2 @Override
3 public void produce() {
4 System.out.println("录制Python课程视频");
5 }
6}
PythonArticle
1public class PythonArticle extends Article {
2 @Override
3 public void produce() {
4 System.out.println("编写Python课程手记");
5 }
6}
PythonCourseFactory
1public class PythonCourseFactory implements CourseFactory {
2 @Override
3 public Video getVideo() {
4 return new PythonVideo();
5 }
6
7 @Override
8 public Article getArticle() {
9 return new PythonArticle();
10 }
11}
Test
1public class Test {
2 public static void main(String[] args) {
3 //创建A产品族的工厂
4 CourseFactory courseFactory = new JavaCourseFactory();
5 //获取产品 A1
6 Video video = courseFactory.getVideo();
7 //获取产品 A2
8 Article article = courseFactory.getArticle();
9
10 video.produce();//录制Java课程视频
11 article.produce();//编写Java课程手记
12 }
13}
案例:MySQL 的 Connection、MyBatis 的 SqlSessionFactory
建造者
流程固定、顺序不固定:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
用户只需指定需要建造的类型就可以得到它们,建造过程及细节不需要知道。
创建型。
建造者模式和工厂模式的区别
建造者模式更注重于方法的调用顺序,工厂模式只关注创建产品。
建造者模式可以用于构建复杂的产品,而工厂模式创造出来的都是一样等级的产品。
建造者模式更专注于创建产品的过程,而工厂模式只要把产品创建出来就行了。
1# 适用场景
2* 如果一个对象有非常复杂的内部结构(很多属性)
3* 想把复杂对象的创建和使用分离
4
5# 优点
6* 封装性好,创建和使用分离
7* 扩展性好、建造类之间独立、一定程度上解耦
8
9# 缺点
10* 产生多余的Builder对象
11* 产品内部发生变化,建造者都要修改,成本较大
Course
1public class Course {
2 private String courseName;
3 private String coursePPT;
4 private String courseVideo;
5 private String courseArticle;
6 //question & answer
7 private String courseQA;
8
9 public String getCourseName() {
10 return courseName;
11 }
12
13 public void setCourseName(String courseName) {
14 this.courseName = courseName;
15 }
16
17 public String getCoursePPT() {
18 return coursePPT;
19 }
20
21 public void setCoursePPT(String coursePPT) {
22 this.coursePPT = coursePPT;
23 }
24
25 public String getCourseVideo() {
26 return courseVideo;
27 }
28
29 public void setCourseVideo(String courseVideo) {
30 this.courseVideo = courseVideo;
31 }
32
33 public String getCourseArticle() {
34 return courseArticle;
35 }
36
37 public void setCourseArticle(String courseArticle) {
38 this.courseArticle = courseArticle;
39 }
40
41 public String getCourseQA() {
42 return courseQA;
43 }
44
45 public void setCourseQA(String courseQA) {
46 this.courseQA = courseQA;
47 }
48
49 @Override
50 public String toString() {
51 return "Course{" +
52 "courseName='" + courseName + '\'' +
53 ", coursePPT='" + coursePPT + '\'' +
54 ", courseVideo='" + courseVideo + '\'' +
55 ", courseArticle='" + courseArticle + '\'' +
56 ", courseQA='" + courseQA + '\'' +
57 '}';
58 }
59}
CourseBuilder
1public abstract class CourseBuilder {
2
3 public abstract void buildCourseName(String courseName);
4 public abstract void buildCoursePPT(String coursePPT);
5 public abstract void buildCourseVideo(String courseVideo);
6 public abstract void buildCourseArticle(String courseArticle);
7 public abstract void buildCourseQA(String courseQA);
8
9 public abstract Course makeCourse();
10}
CourseActualBuilder
1public class CourseActualBuilder extends CourseBuilder {
2
3 private Course course = new Course();
4
5
6 @Override
7 public void buildCourseName(String courseName) {
8 course.setCourseName(courseName);
9 }
10
11 @Override
12 public void buildCoursePPT(String coursePPT) {
13 course.setCoursePPT(coursePPT);
14 }
15
16 @Override
17 public void buildCourseVideo(String courseVideo) {
18 course.setCourseVideo(courseVideo);
19 }
20
21 @Override
22 public void buildCourseArticle(String courseArticle) {
23 course.setCourseArticle(courseArticle);
24 }
25
26 @Override
27 public void buildCourseQA(String courseQA) {
28 course.setCourseQA(courseQA);
29 }
30
31 @Override
32 public Course makeCourse() {
33 return course;
34 }
35}
Coach
1public class Coach {
2 private CourseBuilder courseBuilder;
3
4 public void setCourseBuilder(CourseBuilder courseBuilder) {
5 this.courseBuilder = courseBuilder;
6 }
7
8 public Course makeCourse(String courseName,String coursePPT,
9 String courseVideo,String courseArticle,
10 String courseQA){
11 this.courseBuilder.buildCourseName(courseName);
12 this.courseBuilder.buildCoursePPT(coursePPT);
13 this.courseBuilder.buildCourseVideo(courseVideo);
14 this.courseBuilder.buildCourseArticle(courseArticle);
15 this.courseBuilder.buildCourseQA(courseQA);
16 return this.courseBuilder.makeCourse();
17 }
18}
Test
1public class Test {
2 public static void main(String[] args) {
3 //课程构建类
4 CourseBuilder courseBuilder = new CourseActualBuilder();
5 //建造者
6 Coach coach = new Coach();
7 coach.setCourseBuilder(courseBuilder);
8
9 //建造者通过课程构建类完成课程的具体构建流程
10 Course course = coach.makeCourse("Java设计模式精讲",
11 "Java设计模式精讲PPT",
12 "Java设计模式精讲视频",
13 "Java设计模式精讲手记",
14 "Java设计模式精讲问答");
15 System.out.println(course);//Course{courseName='Java设计模式精讲', coursePPT='Java设计模式精讲PPT', courseVideo='Java设计模式精讲视频', courseArticle='Java设计模式精讲手记', courseQA='Java设计模式精讲问答'}
16 }
17}
或者改进(链式调用)
Course
1public class Course {
2
3 private String courseName;
4 private String coursePPT;
5 private String courseVideo;
6 private String courseArticle;
7
8 //question & answer
9 private String courseQA;
10
11 public Course(CourseBuilder courseBuilder) {
12 this.courseName = courseBuilder.courseName;
13 this.coursePPT = courseBuilder.coursePPT;
14 this.courseVideo = courseBuilder.courseVideo;
15 this.courseArticle = courseBuilder.courseArticle;
16 this.courseQA = courseBuilder.courseQA;
17 }
18
19
20 @Override
21 public String toString() {
22 return "Course{" +
23 "courseName='" + courseName + '\'' +
24 ", coursePPT='" + coursePPT + '\'' +
25 ", courseVideo='" + courseVideo + '\'' +
26 ", courseArticle='" + courseArticle + '\'' +
27 ", courseQA='" + courseQA + '\'' +
28 '}';
29 }
30
31 public static class CourseBuilder{
32 private String courseName;
33 private String coursePPT;
34 private String courseVideo;
35 private String courseArticle;
36
37 //question & answer
38 private String courseQA;
39
40 public CourseBuilder buildCourseName(String courseName){
41 this.courseName = courseName;
42 return this;
43 }
44
45
46 public CourseBuilder buildCoursePPT(String coursePPT) {
47 this.coursePPT = coursePPT;
48 return this;
49 }
50
51 public CourseBuilder buildCourseVideo(String courseVideo) {
52 this.courseVideo = courseVideo;
53 return this;
54 }
55
56 public CourseBuilder buildCourseArticle(String courseArticle) {
57 this.courseArticle = courseArticle;
58 return this;
59 }
60
61 public CourseBuilder buildCourseQA(String courseQA) {
62 this.courseQA = courseQA;
63 return this;
64 }
65
66 /**
67 * 完成 Course 的构造
68 * @return
69 */
70 public Course build(){
71 return new Course(this);
72 }
73
74 }
75}
Test
1public class Test {
2 public static void main(String[] args) {
3 //链式调用
4 Course course = new Course.CourseBuilder().buildCourseName("Java设计模式精讲").buildCoursePPT("Java设计模式精讲PPT").buildCourseVideo("Java设计模式精讲视频").build();
5 System.out.println(course);//Course{courseName='Java设计模式精讲', coursePPT='Java设计模式精讲PPT', courseVideo='Java设计模式精讲视频', courseArticle='null', courseQA='null'}
6
7 }
8}
案例:StringBuilder、StringBuffer、ImmutableSet(Guava)
1 Set<String> set = ImmutableSet.<String>builder().add("a").add("b").build();
2 System.out.println(set);//[a, b]
单例
保证一个类仅有一个实例,并提供一个全局访问点。
创建型
1# 适用场景
2* 想确保任何情况下都绝对只有一个实例
3
4# 优点
5* 在内存里只有一个实例,减少了内存开销
6* 可以避免对资源的多重占用
7* 设置了全局访问点,严格控制访问
8
9# 缺点
10* 没有接口,扩展困难(需要修改代码)
11
12# 重点
13* 私有构造函数
14* 线程安全
15* 延迟加载
16* 序列化和反序列化安全
17* 防御反射攻击
18
19# 单例模式和其他设计模式的结合应用
20* 单例模式和工厂模式
21* 单例模式和享元模式
懒汉式(线程安全:性能太低 —— 同步方法)
1public class LazySingleton {
2 //初始化时不创建(延迟加载)
3 private static LazySingleton lazySingleton = null;
4
5 /**
6 * 私有化无参构造函数
7 */
8 private LazySingleton(){
9 }
10
11 /**
12 * 全局访问点
13 * @return
14 */
15 public synchronized static LazySingleton getInstance(){
16 //空则创建:延迟加载
17 if(lazySingleton == null){
18 lazySingleton = new LazySingleton();
19 }
20 //非空直接返回
21 return lazySingleton;
22 }
懒汉式 (性能优化)
LazyDoubleCheckSingleton
第一种解决思路(双重检查):不允许 第二步、第三步 发生重排序
1public class LazyDoubleCheckSingleton {
2 //不允许:第二步、第三步 (禁止重排序)
3 private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
4 private LazyDoubleCheckSingleton(){
5
6 }
7 public static LazyDoubleCheckSingleton getInstance(){
8 if(lazyDoubleCheckSingleton == null){
9 synchronized (LazyDoubleCheckSingleton.class){
10 if(lazyDoubleCheckSingleton == null){
11 lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
12 //1.分配内存给这个对象
13 //3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
14 //2.初始化对象
15 //intra-thread semantics
16 //---------------//3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
17 }
18 }
19 }
20 return lazyDoubleCheckSingleton;
21 }
22}
第二种解决思路(静态内部类:基于类初始化的延迟加载解决方案):允许发生重排序,但是不允许其他线程看到
1public class StaticInnerClassSingleton {
2 /**
3 * 静态内部类:随类加载而加载
4 */
5 private static class InnerClass{
6 private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
7 }
8
9 /**
10 * 全局访问点
11 * @return
12 */
13 public static StaticInnerClassSingleton getInstance(){
14 return InnerClass.staticInnerClassSingleton;
15 }
16
17 /**
18 * 私有化无参构造函数,如果实例已经存在则抛出异常
19 */
20 private StaticInnerClassSingleton(){
21 if(InnerClass.staticInnerClassSingleton != null){
22 throw new RuntimeException("单例构造器禁止反射调用");
23 }
24 }
25}
饿汉式
1public class HungrySingleton implements Serializable,Cloneable{
2
3 private final static HungrySingleton hungrySingleton;
4
5 //随类加载而初始化
6 static{
7 hungrySingleton = new HungrySingleton();
8 }
9
10 private HungrySingleton(){
11 if(hungrySingleton != null){
12 throw new RuntimeException("单例构造器禁止反射调用");
13 }
14 }
15
16 /**
17 * 全局访问点
18 * @return
19 */
20 public static HungrySingleton getInstance(){
21 return hungrySingleton;
22 }
23}
序列化破坏单例模式的解决方案
HungrySingleton
1public class HungrySingleton implements Serializable,Cloneable{
2
3 private final static HungrySingleton hungrySingleton;
4
5 //随类加载而初始化
6 static{
7 hungrySingleton = new HungrySingleton();
8 }
9
10 private HungrySingleton(){
11 if(hungrySingleton != null){
12 throw new RuntimeException("单例构造器禁止反射调用");
13 }
14 }
15
16 /**
17 * 全局访问点
18 * @return
19 */
20 public static HungrySingleton getInstance(){
21 return hungrySingleton;
22 }
23
24 /**
25 * ObjectInputStream 需要用到
26 * @return
27 */
28 private Object readResolve(){
29 return hungrySingleton;
30 }
31
32 /**
33 *
34 * @return
35 * @throws CloneNotSupportedException
36 */
37 @Override
38 protected Object clone() throws CloneNotSupportedException {
39 return getInstance();
40 }
41}
测试
1 //序列化
2 HungrySingleton instance = HungrySingleton.getInstance();
3 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
4 oos.writeObject(instance);
5
6 //反序列化
7 File file = new File("singleton_file");
8 //需要用到 HungrySingleton 类的 readResolve() 方法
9 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
10 HungrySingleton newInstance = (HungrySingleton) ois.readObject();
11
12 System.out.println(instance);//序列化:HungrySingleton@9807454
13 System.out.println(newInstance);//反序列化:HungrySingleton@9807454
14 System.out.println(instance == newInstance);//true
反射攻击破坏单例模式的解决方案
懒汉式模式下无法阻止反射攻击,反射可以拿到所有成员变量做修改。
饿汉式可以避免方式攻击:通过阻止反射调用私有构造函数
HungrySingleton
1public class HungrySingleton implements Serializable{
2
3 private final static HungrySingleton hungrySingleton;
4
5 //随类加载而初始化
6 static{
7 hungrySingleton = new HungrySingleton();
8 }
9
10 //阻止反射调用私有构造函数:阻止反射攻击
11 private HungrySingleton(){
12 if(hungrySingleton != null){
13 throw new RuntimeException("单例构造器禁止反射调用");
14 }
15 }
16
17 /**
18 * 全局访问点
19 * @return
20 */
21 public static HungrySingleton getInstance(){
22 return hungrySingleton;
23 }
24
25 /**
26 * ObjectInputStream 需要用到
27 * @return
28 */
29 private Object readResolve(){
30 return hungrySingleton;
31 }
32}
测试
1 Class objectClass = HungrySingleton.class;
2 Constructor constructor = objectClass.getDeclaredConstructor();
3 constructor.setAccessible(true);
4
5 HungrySingleton instance = HungrySingleton.getInstance();
6 HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
7
8 System.out.println(instance);
9 System.out.println(newInstance);
10 System.out.println(instance == newInstance);
会抛出异常,防止反射攻击破坏单例模式
1Exception in thread "main" java.lang.reflect.InvocationTargetException
2 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
3 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
4 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
5 at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
6 at com.geely.design.pattern.creational.singleton.Test.main(Test.java:65)
7Caused by: java.lang.RuntimeException: 单例构造器禁止反射调用
8 at com.geely.design.pattern.creational.singleton.HungrySingleton.<init>(HungrySingleton.java:17)
9 ... 5 more
枚举类(最佳实践:可以防止序列化和反射攻击)
1public enum EnumInstance {
2 INSTANCE{
3 protected void printTest(){
4 System.out.println("Geely Print Test");
5 }
6 };
7 protected abstract void printTest();
8
9 private Object data;
10 public Object getData() {
11 return data;
12 }
13 public void setData(Object data) {
14 this.data = data;
15 }
16
17 public static EnumInstance getInstance(){
18 return INSTANCE;
19 }
20}
容器单例
ContainerSingleton
1public class ContainerSingleton {
2
3 private ContainerSingleton(){
4
5 }
6 private static Map<String,Object> singletonMap = new ConcurrentHashMap<String, Object>();
7
8 public static void putInstance(String key,Object instance){
9 if(StringUtils.isNotBlank(key) && instance != null){
10 if(!singletonMap.containsKey(key)){
11 singletonMap.put(key,instance);
12 }
13 }
14 }
15
16 public static Object getInstance(String key){
17 return singletonMap.get(key);
18 }
19}
线程单例(不保证全局唯一,但是可以保证线程唯一)
1同步锁:时间换空间
2ThreadLocal:空间换时间(线程间隔离,每个线程都有一个实例,并不是真正意义上的单例)
ThreadLocalInstance
1public class ThreadLocalInstance {
2 private static final ThreadLocal<ThreadLocalInstance> threadLocalInstanceThreadLocal
3 = new ThreadLocal<ThreadLocalInstance>(){
4 @Override
5 protected ThreadLocalInstance initialValue() {
6 return new ThreadLocalInstance();
7 }
8 };
9 private ThreadLocalInstance(){
10
11 }
12
13 public static ThreadLocalInstance getInstance(){
14 return threadLocalInstanceThreadLocal.get();
15 }
16
17}
案例:RunTime、Desktop
原形模式
原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
不需要知道任何创建的细节,不调用构造函数。
创建型
1# 适用场景
2* 类初始化消耗较多资源
3* new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
4* 构造函数比较复杂
5* 循环体中生产大量对象时
6
7# 优点
8* 原型模式性能比直接new一个对象性能高(在内存中进行二进制流拷贝)
9* 简化创建过程
10
11# 缺点
12* 必须配备克隆方法
13* 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
14* 深拷贝、浅拷贝要运用得当
15
16# 深克隆
17 引用类型指向不同的对象(用于克隆引用类型的成员变量)
18
19# 浅克隆
20 可用于克隆基本类型的成员变量
1public class Mail implements Cloneable{
2 private String name;
3 private String emailAddress;
4 private String content;
5 public Mail(){
6 System.out.println("Mail Class Constructor");
7 }
8
9 public String getName() {
10 return name;
11 }
12
13 public void setName(String name) {
14 this.name = name;
15 }
16
17 public String getEmailAddress() {
18 return emailAddress;
19 }
20
21 public void setEmailAddress(String emailAddress) {
22 this.emailAddress = emailAddress;
23 }
24
25 public String getContent() {
26 return content;
27 }
28
29 public void setContent(String content) {
30 this.content = content;
31 }
32
33 @Override
34 public String toString() {
35 return "Mail{" +
36 "name='" + name + '\'' +
37 ", emailAddress='" + emailAddress + '\'' +
38 ", content='" + content + '\'' +
39 '}'+super.toString();
40 }
41
42 @Override
43 protected Object clone() thro ws CloneNotSupportedException {
44 System.out.println("clone mail object");
45 return super.clone();
46 }
47}
MailUtil
1public class MailUtil {
2 public static void sendMail(Mail mail){
3 String outputContent = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功";
4 System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
5 }
6 public static void saveOriginMailRecord(Mail mail){
7 System.out.println("存储originMail记录,originMail:"+mail.getContent());
8 }
9}
Test
1public class Test {
2 public static void main(String[] args) throws CloneNotSupportedException {
3 //假设 new 一个 Mail 对象非常复杂,非常消耗性能。
4 Mail mail = new Mail();
5 mail.setContent("初始化模板");
6 System.out.println("初始化mail:"+mail);//初始化mail:Mail{name='null', emailAddress='null', content='初始化模板'}com.geely.design.pattern.creational.prototype.Mail@7530d0a
7
8 for(int i = 0;i < 10;i++){
9 //使用原始的mail进行clone
10 Mail mailTemp = (Mail) mail.clone();
11 mailTemp.setName("姓名"+i);
12 mailTemp.setEmailAddress("姓名"+i+"@imooc.com");
13 mailTemp.setContent("恭喜您,此次慕课网活动中奖了");
14 MailUtil.sendMail(mailTemp);
15 System.out.println("克隆的mailTemp:"+mailTemp);
16 }
17
18 MailUtil.saveOriginMailRecord(mail);//存储originMail记录,originMail:初始化模板
19 }
20}
深拷贝(默认是浅克隆)
Pig
1public class Pig implements Cloneable{
2 private String name;
3 private Date birthday;
4
5 public Pig(String name, Date birthday) {
6 this.name = name;
7 this.birthday = birthday;
8 }
9
10 public String getName() {
11 return name;
12 }
13
14 public void setName(String name) {
15 this.name = name;
16 }
17
18 public Date getBirthday() {
19 return birthday;
20 }
21
22 public void setBirthday(Date birthday) {
23 this.birthday = birthday;
24 }
25
26 @Override
27 protected Object clone() throws CloneNotSupportedException {
28 Pig pig = (Pig)super.clone();
29
30 //深克隆
31 pig.birthday = (Date) pig.birthday.clone();
32 return pig;
33 }
34
35 @Override
36 public String toString() {
37 return "Pig{" +
38 "name='" + name + '\'' +
39 ", birthday=" + birthday +
40 '}'+super.toString();
41 }
42}
Test
1public class Test {
2 public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
3 Date birthday = new Date(0L);
4 Pig pig1 = new Pig("佩奇",birthday);
5 Pig pig2 = (Pig) pig1.clone();
6 System.out.println(pig1);
7 System.out.println(pig2);
8
9 pig1.getBirthday().setTime(666666666666L);
10
11 System.out.println(pig1);//Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.geely.design.pattern.creational.prototype.clone.Pig@4fca772d
12 System.out.println(pig2);//Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.geely.design.pattern.creational.prototype.clone.Pig@9807454
13 }
14}
案例:ArrayList、HashMap、CacheKey(Mybatis)
外观模式
又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。
外观模式定义一个高层接口,让子系统更容易使用。
结构型
1# 适用场景
2* 子系统越来越复杂,增加外观模式提供简单调用接口
3* 构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
4
5# 优点
6* 简化了调用过程,无需了解深入子系统,防止带来风险。
7* 减少系统依赖、松散耦合
8* 更好的划分访问层次
9
10# 缺点
11* 增加子系统、扩展子系统行为容易引入风险
12* 不符合开闭原则
13
14# 外观-相关设计模式
15* 外加模式和中介者模式
16* 外观模式和单例模式
17* 外观模式和抽象工厂模式
PointsGift
1public class PointsGift {
2 private String name;
3
4 public PointsGift(String name) {
5 this.name = name;
6 }
7
8 public String getName() {
9 return name;
10 }
11}
QualifyService
1public class QualifyService {
2 public boolean isAvailable(PointsGift pointsGift){
3 System.out.println("校验"+pointsGift.getName()+" 积分资格通过,库存通过");
4 return true;
5 }
6}
PointsPaymentService
1public class PointsPaymentService {
2 public boolean pay(PointsGift pointsGift){
3 //扣减积分
4 System.out.println("支付"+pointsGift.getName()+" 积分成功");
5 return true;
6 }
7}
ShippingService
1public class ShippingService {
2 public String shipGift(PointsGift pointsGift){
3 //物流系统的对接逻辑
4 System.out.println(pointsGift.getName()+"进入物流系统");
5 String shippingOrderNo = "666";
6 return shippingOrderNo;
7 }
8}
GiftExchangeService
1/**
2 * 把多个子系统封装在一起,只对外提供一个外观(门面)类提供服务,调用者无需知道各个子系统的逻辑
3 */
4public class GiftExchangeService {
5 private QualifyService qualifyService = new QualifyService();
6 private PointsPaymentService pointsPaymentService = new PointsPaymentService();
7 private ShippingService shippingService = new ShippingService();
8
9 public void giftExchange(PointsGift pointsGift){
10 if(qualifyService.isAvailable(pointsGift)){
11 //资格校验通过
12 if(pointsPaymentService.pay(pointsGift)){
13 //如果支付积分成功
14 String shippingOrderNo = shippingService.shipGift(pointsGift);
15 System.out.println("物流系统下单成功,订单号是:"+shippingOrderNo);
16 }
17 }
18 }
19}
Test
1public class Test {
2 public static void main(String[] args) {
3 PointsGift pointsGift = new PointsGift("T恤");
4 GiftExchangeService giftExchangeService = new GiftExchangeService();
5 giftExchangeService.giftExchange(pointsGift);
6 }
7}
测试结果
1校验T恤 积分资格通过,库存通过
2支付T恤 积分成功
3T恤进入物流系统
4物流系统下单成功,订单号是:666
案例:JdbcUtils(Spring)、Configuration(Mybatis)、RequestFacade(Tomcat)
装饰者
在不改变原有对象的基础之上,将功能附加到对象上。
提供了比继承更有弹性的替代方案(扩展原有对象功能)。
结构型
1# 使用场景
2* 扩展一个类的功能或给一个类添加附加职责
3* 动态的给一个对象添加功能,这些功能可以再动态的撤销
4
5# 优点
6* 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能(动态扩展功能,运行期间决定增加的功能)
7* 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果
8* 符合开闭原则
9
10# 缺点
11* 会出现更多的代码,更多的类,增加程序复杂性
12* 动态装饰时,多层装饰时会更复杂
13
14# 关联其他的设计模式
15* 装饰者模式、代理模式
16* 装饰者模式、适配器模式
17
ABattercake(被装饰者)
1public abstract class ABattercake {
2 protected abstract String getDesc();
3 protected abstract int cost();
4}
5
Battercake
1public class Battercake extends ABattercake {
2 @Override
3 protected String getDesc() {
4 return "煎饼";
5 }
6
7 @Override
8 protected int cost() {
9 return 8;
10 }
11}
12
AbstractDecorator(抽象装饰者)
1public abstract class AbstractDecorator extends ABattercake {
2 private ABattercake aBattercake;
3
4 public AbstractDecorator(ABattercake aBattercake) {
5 this.aBattercake = aBattercake;
6 }
7
8 protected abstract void doSomething();
9
10 @Override
11 protected String getDesc() {
12 return this.aBattercake.getDesc();
13 }
14
15 @Override
16 protected int cost() {
17 return this.aBattercake.cost();
18 }
19}
20
EggDecorator(鸡蛋实体装饰类)
1public class EggDecorator extends AbstractDecorator {
2 public EggDecorator(ABattercake aBattercake) {
3 super(aBattercake);
4 }
5
6 @Override
7 protected void doSomething() {
8
9 }
10
11 @Override
12 protected String getDesc() {
13 return super.getDesc()+" 加一个鸡蛋";
14 }
15
16 @Override
17 protected int cost() {
18 return super.cost()+1;
19 }
20}
21
SausageDecorator(香肠实体装饰类)
1public class SausageDecorator extends AbstractDecorator{
2 public SausageDecorator(ABattercake aBattercake) {
3 super(aBattercake);
4 }
5
6 @Override
7 protected void doSomething() {
8
9 }
10
11 @Override
12 protected String getDesc() {
13 return super.getDesc()+" 加一根香肠";
14 }
15
16 @Override
17 protected int cost() {
18 return super.cost()+2;
19 }
20}
Test
1public class Test {
2 public static void main(String[] args) {
3 ABattercake aBattercake;
4 aBattercake = new Battercake();
5 aBattercake = new EggDecorator(aBattercake);
6 aBattercake = new EggDecorator(aBattercake);
7 aBattercake = new SausageDecorator(aBattercake);
8
9 System.out.println(aBattercake.getDesc()+" 销售价格:"+aBattercake.cost());
10 //煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠 销售价格:12
11 }
12}
案例
1* BufferedReader:Reader是一个抽象类,BufferedReader装饰Reader.
2* Servlet:SessionRepositoryRequestWrapper.
适配器
将一个类的接口转换成客户期望的另一个接口
使原本接口不兼容的类可以一起工作
结构型
1# 适用场景
2* 已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)
3* 不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。
4
5# 优点
6* 能提高类的透明性和复用,现有的类复用但不需要改变
7* 目标类和适配器类解耦,提高程序扩展性
8* 符合开闭原则
9
10# 缺点
11* 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
12* 增加系统代码可读的难度
13
14# 适配器-扩展
15* 对象适配器
16* 类适配器
方式一:类适配器模式(继承)
Adaptee
1public class Adaptee {
2 public void adapteeRequest(){
3 System.out.println("被适配者的方法");
4 }
5}
Adapter
1public class Adapter extends Adaptee implements Target{
2 @Override
3 public void request() {
4 //...
5 super.adapteeRequest();
6 //...
7 }
8}
ConcreteTarget
1public class ConcreteTarget implements Target {
2 @Override
3 public void request() {
4 System.out.println("concreteTarget目标方法");
5 }
6}
Target
1public interface Target {
2 void request();
3}
Test
1public class Test {
2 public static void main(String[] args) {
3 Target target = new ConcreteTarget();
4 target.request();//concreteTarget目标方法
5
6 Target adapterTarget = new Adapter();
7 adapterTarget.request();//被适配者的方法
8 }
9}
方式二:对象适配器模式(组合优先于继承)
Adaptee
1public class Adaptee {
2 public void adapteeRequest(){
3 System.out.println("被适配者的方法");
4 }
5}
Adapter
1public class Adapter implements Target{
2 private Adaptee adaptee = new Adaptee();
3
4 @Override
5 public void request() {
6 //...
7 adaptee.adapteeRequest();
8 //...
9 }
10}
ConcreteTarget
1public class ConcreteTarget implements Target {
2 @Override
3 public void request() {
4 System.out.println("concreteTarget目标方法");
5 }
6}
Target
1public interface Target {
2 void request();
3}
Test
1public class Test {
2 public static void main(String[] args) {
3 Target target = new ConcreteTarget();
4 target.request();//concreteTarget目标方法
5
6 Target adapterTarget = new Adapter();
7 adapterTarget.request();//被适配者的方法
8 }
9}
220 交流电通过适配器变为直流电 5V
AC220
1public class AC220 {
2 public int outputAC220V(){
3 int output = 220;
4 System.out.println("输出交流电"+output+"V");
5 return output;
6 }
7}
DC5
1public interface DC5 {
2 int outputDC5V();
3}
PowerAdapter
1public class PowerAdapter implements DC5{
2 private AC220 ac220 = new AC220();
3
4 @Override
5 public int outputDC5V() {
6 int adapterInput = ac220.outputAC220V();
7 //变压器...
8 int adapterOutput = adapterInput/44;
9
10 System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V");
11 return adapterOutput;
12 }
13}
Test
1public class Test {
2 public static void main(String[] args) {
3 DC5 dc5 = new PowerAdapter();
4 dc5.outputDC5V();//使用PowerAdapter输入AC:220V输出DC:5V
5 }
6}
案例:XmlAdapter(XML 序列化和反序列化)、JpaVendorAdapter
享元模式
提高了减少对象数量(减少内存占用,提高性能),从而改善应用所需的对象结构的方式。
运用共享技术有效地支持大量细粒度的对象。
结构型。
1# 适用场景
2* 常常应用于系统底层的开发,以便解决系统的性能问题。
3* 系统有大量相似对象、需要缓冲池的场景。
4
5# 优点
6* 减少对象的创建,降低内存中对象的数据量,降低系统的内存,提高效率。
7* 减少内存之外的其他资源占用
8
9# 缺点
10* 关注内/外部状态、关注线程安全问题
11* 使系统、程序的逻辑复杂化
12
13# 扩展
14* 内部状态:享元对象的一个属性,该属性不会随着外部的环境变化而变化。
15* 外部状态:记录在享元对象的外部,不可以被共享的状态。
16
17# 享元模式关联的其他模式
18* 享元模式和代理模式
19* 享元模式和单例模式
Employee
1public interface Employee {
2 void report();
3}
Manager
1public class Manager implements Employee {
2 @Override
3 public void report() {
4 System.out.println(reportContent);
5 }
6 private String title = "部门经理"; //内部状态
7 private String department; //外部状态
8 private String reportContent;
9
10 public void setReportContent(String reportContent) {
11 this.reportContent = reportContent;
12 }
13
14 public Manager(String department) {
15 this.department = department;
16 }
17}
EmployeeFactory
1public class EmployeeFactory {
2 private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<String,Employee>();
3
4 public static Employee getManager(String department){
5 Manager manager = (Manager) EMPLOYEE_MAP.get(department);
6
7 if(manager == null){
8 manager = new Manager(department);
9 System.out.print("创建部门经理:"+department);
10 String reportContent = department+"部门汇报:此次报告的主要内容是......";
11 manager.setReportContent(reportContent);
12 System.out.println(" 创建报告:"+reportContent);
13 EMPLOYEE_MAP.put(department,manager);
14
15 }
16 return manager;
17 }
18}
Test
1public class Test {
2 //部门列表
3 private static final String departments[] = {"RD","QA","PM","BD"};
4
5 public static void main(String[] args) {
6 for(int i=0; i<10; i++){
7 String department = departments[(int)(Math.random() * departments.length)];
8 Manager manager = (Manager) EmployeeFactory.getManager(department);
9 manager.report();
10 }
11 }
12
13}
控制台输出
1创建部门经理:PM 创建报告:PM部门汇报:此次报告的主要内容是......
2PM部门汇报:此次报告的主要内容是......
3创建部门经理:BD 创建报告:BD部门汇报:此次报告的主要内容是......
4BD部门汇报:此次报告的主要内容是......
5PM部门汇报:此次报告的主要内容是......
6BD部门汇报:此次报告的主要内容是......
7创建部门经理:QA 创建报告:QA部门汇报:此次报告的主要内容是......
8QA部门汇报:此次报告的主要内容是......
9BD部门汇报:此次报告的主要内容是......
10BD部门汇报:此次报告的主要内容是......
11QA部门汇报:此次报告的主要内容是......
12创建部门经理:RD 创建报告:RD部门汇报:此次报告的主要内容是......
13RD部门汇报:此次报告的主要内容是......
14QA部门汇报:此次报告的主要内容是......
案例:String、数据库连接池、Integer.valueOf(100)
源码解析
1 public static Integer valueOf(int i) {
2 if (i >= IntegerCache.low && i <= IntegerCache.high)
3 return IntegerCache.cache[i + (-IntegerCache.low)];
4 return new Integer(i);
5 }
测试 Integer.valueOf()
1public class Test {
2
3 Integer a = Integer.valueOf(100);
4 Integer b = 100;
5
6 Integer c = Integer.valueOf(1000);
7 Integer d = 1000;
8
9 System.out.println("a==b:"+(a==b));//a==b:true
10 System.out.println("c==d:"+(c==d));//c==d:false
11 }
12}
组合模式
将对象组合成树形结构以表示 “部分-整体” 的层次结构。
组合模式使客户端对单个对象和组合对象保持一致的方式处理。
结构型
1# 适用场景
2* 希望客户端可以忽略组合对象与单个对象的差异时
3* 处理一个树形结构时
4
5# 优点
6* 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
7* 让客户端忽略了层次的差异,方便对整个层次结构进行控制
8* 简化客户端代码
9* 符合开闭原则
10
11# 缺点
12* 限制类型时会比较复杂
13* 使设计变得更加抽象
14
15# 组合模式-相关设计模式
16* 组合模式和访问者模式
CatalogComponent
1public abstract class CatalogComponent {
2
3 //CourseCatalog
4 public void add(CatalogComponent catalogComponent){
5 throw new UnsupportedOperationException("不支持添加操作");
6 }
7
8 //CourseCatalog
9 public void remove(CatalogComponent catalogComponent){
10 throw new UnsupportedOperationException("不支持删除操作");
11 }
12
13 //Course / CourseCatalog
14 public String getName(CatalogComponent catalogComponent){
15 throw new UnsupportedOperationException("不支持获取名称操作");
16 }
17
18 //Course
19 public double getPrice(CatalogComponent catalogComponent){
20 throw new UnsupportedOperationException("不支持获取价格操作");
21 }
22
23 //Course
24 public void print(){
25 throw new UnsupportedOperationException("不支持打印操作");
26 }
27}
Course
1public class Course extends CatalogComponent {
2
3 private String name;
4 private double price;
5
6 public Course(String name, double price) {
7 this.name = name;
8
9 this.price = price;
10 }
11
12 @Override
13 public String getName(CatalogComponent catalogComponent) {
14 return this.name;
15 }
16
17 @Override
18 public double getPrice(CatalogComponent catalogComponent) {
19 return this.price;
20 }
21
22 @Override
23 public void print() {
24 System.out.println("Course Name:"+name+" Price:"+price);
25 }
26}
CourseCatalog
1public class CourseCatalog extends CatalogComponent {
2 private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
3 private String name;
4 private Integer level;
5
6 public CourseCatalog(String name,Integer level) {
7 this.name = name;
8 this.level = level;
9 }
10
11 @Override
12 public void add(CatalogComponent catalogComponent) {
13 items.add(catalogComponent);
14 }
15
16 @Override
17 public String getName(CatalogComponent catalogComponent) {
18 return this.name;
19 }
20
21 @Override
22 public void remove(CatalogComponent catalogComponent) {
23 items.remove(catalogComponent);
24 }
25
26 @Override
27 public void print() {
28 System.out.println(this.name);
29 for(CatalogComponent catalogComponent : items){
30 if(this.level != null){
31 for(int i = 0; i < this.level; i++){
32 System.out.print(" ");
33 }
34 }
35 catalogComponent.print();
36 }
37 }
38
39}
控制台输出
1
2慕课网课程主目录
3 Course Name:Linux课程 Price:11.0
4 Course Name:Windows课程 Price:11.0
5 Java课程目录
6 Course Name:Java电商一期 Price:55.0
7 Course Name:Java电商二期 Price:66.0
8 Course Name:Java设计模式 Price:77.0
案例:awt.Container、HashMap.putAll()、ArrayList.addAll()
桥接模式
将抽象部分与它的具体实现部分分离,使它们都可以独立地变化(一定程度上实现解耦)。
通过组合的方式建立两个类之间联系,而不是继承。
结构型
1# 适用场景
2* 抽象和具体实现之间增加更多的灵活性。
3* 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
4* 不希望使用继承,或因为多层继承导致系统类的个数剧增。
5
6# 优点
7* 分离抽象部分及其具体实现部分
8* 提高了系统的可扩展性
9* 符合开闭原则
10* 符合合成复用原则
11* 组合优于继承
12
13# 缺点
14* 增加系统的理解与设计难度
15* 需要正确地识别出系统中两个独立变化的维度
16
17# 桥接 - 相关设计模式
18* 桥接模式和组合模式
19* 桥接模式和适配器模式
Account
1public interface Account {
2 Account openAccount();
3 void showAccountType();
4}
DepositAccount
1public class DepositAccount implements Account {
2 @Override
3 public Account openAccount() {
4 System.out.println("打开定期账号");
5 return new DepositAccount();
6 }
7
8 @Override
9 public void showAccountType() {
10 System.out.println("这是一个定期账号");
11 }
12}
SavingAccount
1public class SavingAccount implements Account {
2 @Override
3 public Account openAccount() {
4 System.out.println("打开活期账号");
5 //...
6 return new SavingAccount();
7 }
8
9 @Override
10 public void showAccountType() {
11 System.out.println("这是一个活期账号");
12 }
13}
Bank
1public abstract class Bank {
2 protected Account account;
3 public Bank(Account account){
4 this.account = account;
5 }
6 abstract Account openAccount();
7}
ABCBank
1public class ABCBank extends Bank {
2 public ABCBank(Account account) {
3 super(account);
4 }
5
6 @Override
7 Account openAccount() {
8 System.out.println("打开中国农业银行账号");
9 account.openAccount();//委托业务实现
10 return account;
11 }
12}
ICBCBank
1public class ICBCBank extends Bank {
2 public ICBCBank(Account account) {
3 super(account);
4 }
5
6 @Override
7 Account openAccount() {
8 System.out.println("打开中国工商银行账号");
9 account.openAccount();//委托业务实现
10 return account;
11 }
12}
Test
1public class Test {
2 public static void main(String[] args) {
3 Bank icbcBank = new ICBCBank(new DepositAccount());
4 Account icbcAccount = icbcBank.openAccount();
5 icbcAccount.showAccountType();
6
7 Bank icbcBank2 = new ICBCBank(new SavingAccount());
8 Account icbcAccount2 = icbcBank2.openAccount();
9 icbcAccount2.showAccountType();
10
11 Bank abcBank = new ABCBank(new SavingAccount());
12 Account abcAccount = abcBank.openAccount();
13 abcAccount.showAccountType();
14 }
15}
控制台输出
1打开中国工商银行账号
2打开定期账号
3这是一个定期账号
4
5打开中国工商银行账号
6打开活期账号
7这是一个活期账号
8
9打开中国农业银行账号
10打开活期账号
11这是一个活期账号
案例:Driver(JDBC,MySQL、Oracle 都实现了 Driver 接口)、
代理模式
为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客户端和目标对象之间起到中介的作用。
结构型
1# 适用场景
2* 保护目标对象(客户端无需于目标对象直接接触,而是和代理对象直接接触)
3* 增强目标对象
4
5# 优点
6* 代理模式能将代理对象与真实被调用的目标对象分离
7* 一定程度上降低了系统的耦合度,扩展性好
8* 保护目标对象
9* 增强目标对象
10
11# 缺点
12* 会造成系统设计中类的数目增加
13* 在客户端和目标对象之间引入代理对象,会造成请求处理速度变慢
14* 增加系统的复杂度
15
16# 扩展
17* 静态代理
18* 动态代理(JDK动态代理只支持实现接口的类、CGLib可以代理类,但不支持final修饰的类或方法)
19
20# Spring 代理选择
21* 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
22* 当 Bean 没有实现接口时,Spring 使用 CGlib
23* 可以强制使用 CGlib
24 在 spring 配置中加入 <aop:aspectj-autoproxy proxy-target-class="true"/>
25
26# 代理速度对比
27* CGLib:底层通过ASM字节码生成,性能高于JAVA反射。
28* JDK动态代理:JAVA反射。
29* JDK8 的 JDK动态代理 性能大约比 CGLib 快20%左右。
30
31# 代理 - 相关设计模式
32* 代理模式和装饰模式
33* 代理模式和适配器模式