设计模式基础-1 概述 在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
==对接口编程而不是对实现编程。==
这样一个方法B在调用A的时候,不是调用的实现(A1,A2,A3),而是调用的接口(A),通过多态性,根据调用者的不同而调用不同的实现方法
==优先使用对象组合而不是继承。==
java规定只能有一个父类
设计模式的基石
总览
创建型模式(Creational Patterns)
单例(Singleton)模式
原型(Prototype)模式
工厂方法(FactoryMethod)模式
抽象工厂(AbstractFactory)模式
建造者(Builder)模式
结构型模式(Structural Patterns)
代理(Proxy)模式
适配器(Adapter)模式
桥接(Bridge)模式
装饰(Decorator)模式
外观(Facade)模式
享元(Flyweight)模式
组合(Composite)模式
过滤器模式(Filter Pattern)
行为型模式(Behavioral Patterns)
模板方法(Template Method)模式.
策略(Strategy)模式
命令(Command)模式
职责链(Chain of Responsibility)模式
状态(State)模式
观察者(Observer)模式
中介者(Mediator)模式
迭代器(Iterator)模式
访问者(Visitor)模式
备忘录(Memento)模式
解释器(Interpreter)模式
设计模式的七大原则 开闭原则 (Open Closed Principle,OCP)
软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification)
合成复用原则、里氏替换原则相辅相成,都是开闭原则的具体实现规范
==扩展新类而不是修改旧类==
里氏替换原则 (Liskov Substitution Principle,LSP)
继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)
==继承父类而不去改变父类==
依赖倒置原则 (Dependence Inversion Principle,DIP)
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions) ==面向接口编程,而不是面向实现类==
单一职责原则 (Single Responsibility Principle,SRP)
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)
==每个类只负责自己的事情,而不是变成万能==
接口隔离原则 (Interface Segregation Principle,ISP)
一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
==各个类建立自己的专用接口,而不是建立万能接口==
迪米特法则 (Law of Demeter,LoD)
最少知识原则(Least Knowledge Principle,LKP) 只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)
==无需直接交互的两个类,如果需要交互,使用中间者==
过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低
合成复用原则 (Composite Reuse Principle,CRP)
又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP) 软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
==优先组合,其次继承==
创建型模式-5 (Creational Patters)
概述 创建型模式关注点“怎样创建出对象?”
“将对象的创建与使用分离”。
降低系统的耦合度
使用者无需关注对象的创建细节
对象的创建由相关的工厂来完成;(各种工厂模式)
对象的创建由一个建造者来完成;(建造者模式)
对象的创建由原来对象克隆完成;(原型模式)
对象始终在系统中只有一个实例;(单例模式)
单例模式 什么是单例模式 单例模式是一种常用的软件设计模式,其定义是==单例对象的类只能允许一个实例存在==。
许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
单例模式应用场景 举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。
继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。
再举一个例子,网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制
同样,对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。
从上述的例子中我们可以总结出适合使用单例模式的场景和优缺点:
适用场景:
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境
例如:
多线程中的线程池
数据库的连接池
系统环境信息
上下文(ServletContext)
……
单例模式的优缺点 优点 :
在内存中只有一个对象,节省内存空间;
避免频繁的创建销毁对象,可以提高性能;
避免对共享资源的多重占用,简化访问;
为整个系统提供一个全局访问点。
缺点 :
不适用于变化频繁的对象;
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
单例模式实现 饿汉式 jvm加载类的的时候就把单例对象生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Singleton1 { private static Singleton1 instance = new Singleton1(); private Singleton1 () {} public static Singleton1 getInstance () { return instance; } }
我们知道,类加载的方式是按需加载,且加载一次 。。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,jvm底层保证了单例 。
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果 。如果从始至终从未使用过这个实例,则会造成内存的浪费。
懒汉式 jvm加载类的的时候不生成单例对象,而是需要的时候才第一次创建,线程不安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Singleton2 { private static Singleton2 instance; private Singleton2 () {} public static Singleton2 getInstance () { if (instance == null ) { instance = new Singleton2(); } return instance; } }
们从懒汉式单例可以看到,单例实例被延迟加载 ,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (instance== null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
线程安全饿汉式之全锁 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Singleton3 { private static Singleton3 instance; public static synchronized Singleton3 getInstance () { if (instance == null ) { instance = new Singleton3(); } return instance; } }
优点:保证了线程安全和懒加载
缺点:效率低。这种模式保证的线程安全,但是效率特别低,在高并发情况下,每次调用getInstance方法都需要加锁,显然不符合高并发情况的效率。
线程安全饿汉式之部分锁+双重判空检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Singleton4 { private static Singleton4 instance; public static Singleton4 getInstance () { if (instance == null ) { synchronized (Singleton4.class) { if (instance == null ) { instance = new Singleton4(); } } } return instance; } }
如果发生了重排序(在高并发情况下小概率出现,但是确实会出现):
1 2 3 4 5 6 7 8 9 10 11 instance = new Singleton4(); 1 memory=allocate();2 ctorInstanc(memory) 3 s=memory 1 memory=allocate();3 s=memory 2 ctorInstanc(memory)
而一旦假设发生了这样的重排序,比如线程A在第10行执行了步骤1和步骤3,但是步骤2还没有执行完。这个时候另一个线程B执行到了第7行,它会判定instance不为空,然后直接返回了一个未初始化完成的instance!
优点:保证了加锁的效率,线程安全和懒加载
缺点:在高并发情况下,小概率出现实例为空(或者一个默认值)的情况
线程安全饿汉式之双重锁检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Singleton5 { private static volatile Singleton5 instance; public static Singleton5 getInstance () { if (instance == null ) { synchronized (Singleton5.class) { if (instance == null ) { instance = new Singleton5(); } } } return instance; } }
这里使用volatile禁止了instance = new Singleton5(); 中发生的指令重排序,保证了线程安全。
静态内部类 利用了jvm底层保证只能创建一次,保证了线程安全
类的装载机制保证初始化实例的时候只有一个线程,静态内部类在Singleton被装载时不会立即初始化,而是在调用getInstance时才会装在静态内部类,从而完成Singleton的实例化。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Singleton6 { public Singleton () { } public void doAction () { } private static class SingletonInstance { private final static Singleton SINGLETON = new Singleton(); } public static Singleton getInstance () { return SingletonInstance.SINGLETON; } }
优点:
利用JVM加载静态内部类的机制保证多线程安全
实现Lazy loading效果
效率高
枚举实现单例模式 在effective java(这本书真的很棒)中说道,最佳的单例实现模式就是枚举模式。利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。而且枚举还防止了序列化。
利用序列化也就是反射,能直接更改一个类的任意属性,但是如果使用枚举类,可以避免这种情况。因为枚举类在编译后是abstract class
,是没有构造方法的,所有无法通过一个构造方法去创建一个对象,所以即使序列化后的对象也是之前的单例对象。
1 2 3 4 5 6 7 public enum Singleton7 { INSTANCE; public void doSomething () { System.out.println("doSomething" ); } }
调用方法:
1 public class Main { public static void main (String[] args) { Singleton7.INSTANCE.doSomething(); }}
优点:
线程安全(枚举实例的创建默认就是线程安全的)
不会因为序列化而产生新实例
防止反射攻击
单例防止反射 以双重检测方式为例测试反射,序列化,克隆是否能破环单例模式:
1 public class Singleton5 implements Serializable , Cloneable { private static volatile Singleton5 instance;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class Singleton5Reflect { public static void main (String[] args) throws Exception { Singleton5 singleton = Singleton5.getInstance(); System.out.println("singleton的hashCode:" +singleton.hashCode()); Constructor<Singleton5> constructor = Singleton5.class.getDeclaredConstructor(); constructor.setAccessible(true ); Singleton5 reflex = constructor.newInstance(); System.out.println("reflex的hashCode:" +reflex.hashCode()); Singleton5 clob = (Singleton5)Singleton5.getInstance().clone(); System.out.println("clob的hashCode:" +clob.hashCode()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(Singleton5.getInstance()); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Singleton5 serialize = (Singleton5)ois.readObject(); ois.close(); bis.close(); oos.close(); bos.close(); System.out.println("serialize的hashCode:" +serialize.hashCode()); } }
1 2 3 4 singleton的hashCode:1163157884 reflex的hashCode:1956725890 clob的hashCode:356573597 serialize的hashCode:2074407503
防止反射可以通过一个变量来控制,但是反射也能修改变量,如果先修改变量在创建实例,还是不能防止反射,所以到现在为止最好的方法就是使用枚举来创建防止反射的单例模式
参考文章:https://www.cnblogs.com/call-me-pengye/p/11169051.html
单例模式面试题 系统环境信息(System.getProperties())?
Spring中怎么保持组件单例的?
ServletContext是什么(封装Servlet的信息)?是单例吗?怎么保证?
ApplicationContext是什么?是单例吗?怎么保证?
* ApplicationContext: tomcat:一个应用(部署的一个war包)会有一个应用上下文
* ApplicationContext: Spring:表示整个IOC容器(怎么保证单例的)。ioc容器中有很多组件(怎么保证单例)
数据库连接池一般怎么创建出来的,怎么保证单实例?
原型模式 什么是原型模式 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。
本体给外部提供一个克隆体进行使用
原型模式在java中很好理解,就是java中的深拷贝(复制对象中的所有对象,全新复制体)和浅拷贝(复制对象,但是对象中的引用还是共用一个)。
浅克隆:
如果原型对象的成员变量是值类型(八大基本类型,byte,short,int,long,char,double,float,boolean).那么就直接复制,如果是复杂的类型,(枚举,String,对象)就只复制对应的内存地址。
深克隆:
全部复制,然后各自独立。你修改克隆对象对于原型对象没有丝毫影响
原型对象自己不仅是个对象还是个工厂!并且通过克隆方式创建的对象是全新的对象,它们都是有自己的新的地址,通常对克隆模式所产生的新对象进行修改是不会对原型对象造成任何影响的!,每一个克隆对象都是相对独立的,通过不同的方式对克隆对象进行修改后,可以的到一系列相似但不完全相同的对象。
原型模式分三个角色,抽象原型类,具体原型类,客户类。
抽象原型类(prototype):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是接口,抽象类甚至是一个具体的实现类。
具体原型类(concretePrototype):它实现了抽象原型类中声明的克隆方法,在克隆方法中返回一个自己的克隆对象。
客户类(Client):在客户类中,使用原型对象只需要通过工厂方式创建或者直接NEW(实例化一个)原型对象,然后通过原型对象的克隆方法就能获得多个相同的对象。由于客户端是针对抽象原型对象编程的所以还可以可以很方便的换成不同类型的原型对象!
应用场景
创建对象成本比较大,比如初始化要很长时间的,占用太多CPU的,新对象可以通过复制已有的对象获得的,如果是相似的对象,则可以对其成员变量稍作修改。
系统要保存对象状态的,而对象的状态改变很小。
需要避免使用分层次的工厂类来创建分层次的对象,并且类的对象就只用一个或很少的组合状态!
优缺点 优点
当创建的对象实例较为复杂的时候,使用原型模式可以简化对象的创建过程!
扩展性好,由于写原型模式的时候使用了抽象原型类,在客户端进行编程的时候可以将具体的原型类通过配置进行读取。
可以使用深度克隆来保存对象的状态,使用原型模式进行复制。当你需要恢复到某一时刻就直接跳到。比如我们的idea种就有历史版本,或则SVN中也有这样的操作。
缺点
需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的里面,当对已有的类经行改造时需要修改源代码,违背了开闭原则。
在实现深克隆的时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用的时候,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现相对麻烦。
实现 在java中实现Cloneable,标志着能够克隆对象,然后重写clone()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package top.lvxiaoyi.creation.prototype;public class User implements Cloneable { private String username; private Integer age; public User () { System.out.println("User对象创建" ); } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public Integer getAge () { return age; } public void setAge (Integer age) { this .age = age; } @Override public String toString () { return "User{" + "username='" + username + '\'' + ", age=" + age + '}' ; } @Override protected Object clone () throws CloneNotSupportedException { User user = new User(); user.setUsername(username); user.setAge(age); return user; } }
1 2 3 4 5 6 7 8 9 private User getUserFromDb (String username) throws Exception { System.out.println("从数据库查到:" +username); User user = new User(); user.setUsername(username); user.setAge(18 ); userCache.put(username, (User) user.clone()); return user; }
工厂模式(工厂方法和抽象工厂) 什么是工厂模式 工厂模式(Factory Pattern)提供了一种创建对象的最佳方式。我们不必关心对象的创建细节,只需要根据不同情况获取不同产品即可。难点:写好我们的工厂
工厂模式分为三种:简单工厂模式、方法工厂模式、抽象工厂模式
实现 简单工厂模式 简单工厂模式是一种创建型模式,又叫静态方法工厂模式,是通过定义一个类用来创建其他的类,被创建的类通常都具有相同的父类。简单工厂设计模式相当于是一个工厂中有各种不同的产品,创建在一个类中,调用者无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public abstract class AbstractCar { String engine; public abstract void run () ; } public class MainTest { public static void main (String[] args) { WuLinSimpleFactory factory = new WuLinSimpleFactory(); AbstractCar van = factory.newCar(new VanCar()); AbstractCar mini = factory.newCar(new MiniCar()); AbstractCar zz = factory.newCar("zz" ); van.run(); mini.run(); } } public class MiniCar extends AbstractCar { public MiniCar () { this .engine = "四缸水平对置发动机" ; } @Override public void run () { System.out.println(engine+"--> 嘟嘟嘟..." ); } } public class VanCar extends AbstractCar { public VanCar () { this .engine = "单杠柴油机" ; } @Override public void run () { System.out.println(engine+"--》嗒嗒嗒...." ); } } public class WuLinSimpleFactory { public AbstractCar newCar (Object className) { if (className instanceof VanCar){ return new VanCar(); }else if (className instanceof MiniCar){ return new MiniCar(); } return null ; } }
优点: 工厂类含有必要的判断逻辑,调用者给出信息后通过工厂类来决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象,有利于整个软件体系结构的优化。
缺点: 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
应用场景 工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。
工厂方法 方法工厂模式其实非常简单,和简单工厂模式差不多。简单工厂模式时同一个工厂根据你输入的类型返回不同的汽车,而工厂方法模式是通过不同的对象而创建出不同的工厂,单独生产,也就是说,以原来的工厂为模型==创建不同的工厂生产不同的商品==。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public abstract class AbstractCarFactory { public abstract AbstractCar newCar () ; } public class WulinMinCarFactory extends AbstractCarFactory { @Override public AbstractCar newCar () { return new MiniCar(); } } public class WulinVanCarFactory extends AbstractCarFactory { @Override public AbstractCar newCar () { return new VanCar(); } } public class WulinRacingCarFactory extends AbstractCarFactory { @Override public AbstractCar newCar () { return new RacingCar(); } } public class MainTest { public static void main (String[] args) { AbstractCarFactory carFactory = new WulinRacingCarFactory(); AbstractCar abstractCar = carFactory.newCar(); abstractCar.run(); carFactory = new WulinVanCarFactory(); AbstractCar abstractCar1 = carFactory.newCar(); abstractCar1.run(); } }
优点: 工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,即它有非常良好的拓展性
缺点: 假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。比如说,==每增加一个产品,相应的也要增加一个子工厂,会加大了额外的开发量==。
应用场景 当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法,支持多扩展少修改的OCP原则
抽象工厂模式 抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式,是工厂方法模式的升级版,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public abstract class WulinFactory { List<String> rules; abstract AbstractCar newCar () ; abstract AbstractMask newMask () ; } public abstract class WulinCarFactory extends WulinFactory { @Override abstract AbstractCar newCar () ; @Override AbstractMask newMask () { return null ; } } public abstract class WulinMaskFactory extends WulinFactory { @Override AbstractCar newCar () { return null ; } @Override abstract AbstractMask newMask () ; } public class WulinHangZhouMaskFactory extends WulinMaskFactory { @Override AbstractMask newMask () { return new CommonMask(); } }public class WulinWuHanMaskFactory extends WulinMaskFactory { @Override AbstractMask newMask () { return new N95Mask(); } } public class WulinRacingCarFactory extends WulinCarFactory { @Override AbstractCar newCar () { return new RacingCar(); } } public class WulinVanCarFactory extends WulinCarFactory { @Override AbstractCar newCar () { return new VanCar(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MainTest { public static void main (String[] args) { WulinFactory wulinFactory = new WulinWuHanMaskFactory(); AbstractCar abstractCar = wulinFactory.newCar(); AbstractMask abstractMask = wulinFactory.newMask(); abstractMask.protectedMe(); wulinFactory = new WulinHangZhouMaskFactory(); AbstractMask abstractMask1 = wulinFactory.newMask(); abstractMask1.protectedMe(); } }
优点
抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。(不过说这个缺点好像有点吹毛求疵了)
应用场景 适用于产品工厂较多且会发生变化的的场景
NumberFormat、SimpleDateFormat
LoggerFactory:
SqlSessionFactory:MyBatis
BeanFactory:Spring的BeanFactory(就是为了造出bean)
……
工厂模式的退化
当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
建造者模式 是什么 创建者模式又叫建造者模式,是将一个复杂的对象的构建 与它的表示 分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。
简而言之:创建的东西细节复杂,还必须暴露给使用者。
适用场景:
隔离复杂对象的创建和使用,相同的方法,不同执行顺序,产生不同事件结果
多个部件都可以装配到一个对象中,但产生的运行结果不相同
产品类非常复杂或者产品类因为调用顺序不同而产生不同作用
初始化一个对象时,参数过多,或者很多参数具有默认值
Builder模式不适合创建差异性很大的产品类 产品内部变化复杂,会导致需要定义很多具体建造者类实现变化,增加项目中类的数量,增加系统的理解难度和运行成本
需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
StringBuilder:append(); 给谁append呢?
Swagger-ApiBuilder:
快速实现。Lombok-Builder模式
优缺点 Builder模式:优点是可以将构造器的setter方法名取成类似注释的方式,这样我们可以很清晰的知道刚才究竟设置的什么值,可读性较高,缺点是比较冗长。
优点
使用建造者模式可以使客户端不必知道产品内部组成的细节。
具体的建造者类之间是相互独立的,这有利于系统的扩展。
具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。
缺点
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 import lombok.*;@Builder public class Phone { protected String cpu; protected String mem; protected String disk; protected String cam; @Override public String toString () { return "Phone{" + "cpu='" + cpu + '\'' + ", mem='" + mem + '\'' + ", disk='" + disk + '\'' + ", cam='" + cam + '\'' + '}' ; } public String getCpu () { return cpu; } public String getMem () { return mem; } public String getDisk () { return disk; } public String getCam () { return cam; } } public abstract class AbstractBuilder { Phone phone; abstract AbstractBuilder customCpu (String cpu) ; abstract AbstractBuilder customMem (String mem) ; abstract AbstractBuilder customDisk (String disk) ; abstract AbstractBuilder customCam (String cam) ; Phone getProduct () { return phone; } } public class XiaomiBuilder extends AbstractBuilder { public XiaomiBuilder () { phone = Phone.builder().build(); } @Override AbstractBuilder customCpu (String cpu) { phone.cpu = cpu; return this ; } @Override AbstractBuilder customMem (String mem) { phone.mem = mem; return this ; } @Override AbstractBuilder customDisk (String disk) { phone.disk = disk; return this ; } @Override AbstractBuilder customCam (String cam) { phone.cam = cam; return this ; } } public class MainTest { public static void main (String[] args) { AbstractBuilder builder = new XiaomiBuilder(); Phone phone = builder.customCpu("骁龙8个8" ) .customCam("2亿" ) .customDisk("1T" ) .customMem("16G" ) .getProduct(); System.out.println(phone); Phone build = Phone.builder() .cpu("1" ) .mem("2" ) .cam("3" ) .disk("4" ) .build(); System.out.println(build); } }
建造者模式与抽象工厂模式的比较:
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象 。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车
结构型模式-7 (Struictural Patterns)
概述 结构型模式关注点“==怎样组合对象/类==”所以我们关注下类的组合关系
类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承)
对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)
根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
适配器模式(Adapter Pattern):两个不兼容接口之间适配的桥梁
桥接模式(Bridge Pattern):相同功能抽象化与实现化解耦,抽象与实现可以独立升级。
过滤器模式(Filter、Criteria Pattern):使用不同的标准来过滤一组对象
组合模式(Composite Pattern):相似对象进行组合,形成树形结构
装饰器模式(Decorator Pattern):向一个现有的对象添加新的功能,同时又不改变其结构
外观模式(Facade Pattern):向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性。
享元模式(Flyweight Pattern):尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
代理模式(Proxy Pattern):一个类代表另一个类的功能
适配器模式 Adapter Pattern
什么是适配器模式 ==将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作==,适配器模式分为类结构型模式(继承)和对象结构型模式(组合)两种,前者(继承)类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
别名也可以是Wrapper,包装器
适配器模式(Adapter)包含以下主要角色 :
应用场景
封装有缺陷的接口设计
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。
统一多个类的接口设计
某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。
替换依赖的外部系统
当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。
兼容老版本接口
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景。
适配不同格式的数据
适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之间的适配。比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方便存储和使用。再比如,Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类型的数据转化为集合容器类型。
Tomcat如何将Request流转为标准Request;
tomcat.Request接口
servlet.Request接口
tomcat === CoyoteAdapte === ServletRequest
Spring AOP中的AdvisorAdapter是什么:增强的适配器
前置、后置、返回、结束 Advisor(通知方法)
底层真的目标方法
Spring MVC中经典的HandlerAdapter是什么; HelloController.hello()
HandlerAdapter
Servlet.doGet()
SpringBoot 中 WebMvcConfigurerAdapter为什么存在又取消
优缺点 适配器模式有两种实现方式:类适配器和对象适配器
类适配方式:
优点
高扩展性和复用率:类适配器直接借助了继承,让程序变得灵活,让被继承的类的api复用率很高。
符合开闭原则,任意新建适配类,不用修改基类的代码。
缺点
单继承 也就是java语言本身继承的缺点 : 单继承结构。基类中的子类无法访问到。 ps:如果能做到这点就显得特别强大了。
对象适配(组合)方式:
优点 : 相对类适配继承方式,显然访问范围更广,通过对象可以访问类中的各个属性方法。也是面向接口编程的一个思想体现。
缺点 : 由于整个对象强引入,耦合度高。一旦类中方法修改,比较不好改。
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public interface Player { String play () ; } public interface Translator { String translate (String content) ; } public class Zh_JPTranslator implements Translator { @Override public String translate (String content) { if ("你好" .equals(content)){ return "空尼几哇" ; } if ("什么" .equals(content)){ return "纳尼" ; } return "*******" ; } } public class MoviePlayer implements Player { @Override public String play () { System.out.println("正在播放:宋老师的宝贵时间.avi" ); String content = "你好" ; System.out.println(content); return content; } }
类适配器:
继承原来的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class JPMoviePlayerAdapter extends Zh_JPTranslator implements Player { private Player target; public JPMoviePlayerAdapter (Player target) { this .target = target; } @Override public String play () { String play = target.play(); String translate = translate(play); System.out.println("日文:" +translate); return play; } } public class MainTest { public static void main (String[] args) { JPMoviePlayerAdapter adapter = new JPMoviePlayerAdapter(new MoviePlayer()); adapter.play(); } }
对象适配器:
组合原来的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class JPMoviePlayerAdapter implements Player { private Translator translator = new Zh_JPTranslator(); private Player target; public JPMoviePlayerAdapter (Player target) { this .target = target; } @Override public String play () { String play = target.play(); String translate = translator.translate(play); System.out.println("日文:" +translate); return play; } } public class MainTest { public static void main (String[] args) { JPMoviePlayerAdapter adapter = new JPMoviePlayerAdapter(new MoviePlayer()); adapter.play(); } }
桥接模式 Bridge Pattern
是什么 将抽象与实现解耦,使两者都可以独立变化
在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。如何设计类似于 Photoshop 这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式,m 种形状和 n 种颜色的图形就有 m×n 种,不但对应的子类很多,而且扩展困难。不同颜色和字体的文字、不同品牌和功率的汽车
桥接将继承转为关联,降低类之间的耦合度,减少代码量
桥接(Bridge)模式包含以下主要角色。 系统设计期间,如果这个类里面的一些东西,会扩展很多,这个东西就应该分离出来 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
应用场景 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
InputStreamReader桥接模式。An InputStreamReader is a bridge from byte streams to character streams:
InputStreamReader 桥接+适配器
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public abstract class AbstractPhone { AbstractSale sale; abstract String getPhone () ; public void setSale (AbstractSale sale) { this .sale = sale; } } public abstract class AbstractSale { private String type; private Integer price; public AbstractSale (String type,Integer price) { this .type = type; this .price = price; } String getSaleInfo () { return "渠道:" +type+"==>" +"价格:" +price; } void howToSale () { } } public class IPhone extends AbstractPhone { @Override String getPhone () { return "IPhone:" +sale.getSaleInfo(); } }public class MiPhone extends AbstractPhone { @Override String getPhone () { return "小米:" ; } } public class OfflineSale extends AbstractSale { public OfflineSale (String type, Integer price) { super (type, price); } } public class OnlineSale extends AbstractSale { public OnlineSale (String type, Integer price) { super (type, price); } } public class StudentSale extends AbstractSale { public StudentSale (String type, Integer price) { super (type, price); } }
装饰器模式 是什么 适配器是连接两个类,可以增强一个类;装饰器是增强一个类
向一个现有的对象添加新的功能,同时又不改变其结构。属于对象结构型模式。
创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
应用场景
无处不在…..
SpringSession中如何进行session与redis关联?HttpRequestWrapper
session:数据存在了内存
session:数据存在redis
HttpSession;getAttribute();
Wrapper(session){ getAttribute(String param){ redis.get(param) }; }
MyBatisPlus提取了QueryWrapper,这是什么?
Spring中的BeanWrapper是做什么?包装了Bean。bean的功能增强?
Spring Webflux中的 WebHandlerDecorator?
已存的类,每一天在某个功能使用的时候发现不够,就可以装饰器。
优缺点 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public interface ManTikTok { void tiktok () ; } public class LvTikTok implements ManTikTok { @Override public void tiktok () { System.out.println("吕小医,tiktok.... " ); } } public interface TiktokDecorator extends ManTikTok { void enable () ; } public class MeiYanDecorator implements TiktokDecorator { private ManTikTok manTikTok; public MeiYanDecorator (ManTikTok manTikTok) { this .manTikTok = manTikTok; } @Override public void tiktok () { enable(); manTikTok.tiktok(); } @Override public void enable () { System.out.println("看这个美女....." ); System.out.println("花花花花花花花花花花花" ); } } public class ZhangTikTok implements ManTikTok { @Override public void tiktok () { System.out.println("张三,tiktok.... " ); } } public class MainTest { public static void main (String[] args) { ManTikTok manTikTok = new LeiTikTok(); MeiYanDecorator decorator = new MeiYanDecorator(manTikTok); decorator.tiktok(); } }
代理模式(静态代理和动态代理) 是什么 代理模式(Proxy Pattern) ,给某一个对象提供一个代理,并由代理对象控制对原对象的引用,对象结构型模式。这种也是静态代理
代理模式包含如下角色:
Subject: 抽象主体角色(抽象类或接口)
Proxy: 代理主体角色(代理对象类)
RealSubject: 真实主体角色(被代理对象类)
应用场景
MyBatis的mapper到底是什么?怎么生成的?
动态代理
UserMapper、CityMapper,mybatis帮我们写实现MapperProxy
Alibaba Seata的DataSourceProxy是什么?
DruidDataSource存在的Proxy模式
监控链…
……
实现 代理模式分为两种:静态代理和动态代理
静态代理就是装饰器,装饰模式是代理模式的一种
静态代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public interface ManTikTok { void tiktok () ; } public class LvTikTok implements ManTikTok { @Override public void tiktok () { } } public class LiMingTiktokProxy implements ManTikTok { private ManTikTok manTikTok; public LiMingTiktokProxy (ManTikTok manTikTok) { this .manTikTok = manTikTok; } @Override public void tiktok () { System.out.println("渲染直播间...." ); System.out.println("课程只要666,仅此一天...." ); manTikTok.tiktok(); } } public class MainTest { public static void main (String[] args) { LiMingTiktokProxy proxy = new LiMingTiktokProxy(new LvTikTok()); proxy.tiktok(); } }
动态代理之Proxy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 public interface ManTikTok { void tiktok () ; } public class LvTikTok implements ManTikTok ,SellTikTok { @Override public void tiktok () { System.out.println("吕小医,tiktok.... " ); } @Override public void sell () { System.out.println("吕小医,只要666,赶紧来包..." ); } public void haha () { System.out.println("hahaha ,......" ); } } public class JdkTiktokProxy <T > implements InvocationHandler private T target ; JdkTiktokProxy(T target) { this .target = target; } public static <T> T getProxy (T t) { Object o = Proxy.newProxyInstance( t.getClass().getClassLoader(), t.getClass().getInterfaces(), new JdkTiktokProxy(t)); return (T) o; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("真正执行被代理对象的方法" ); Object invoke = method.invoke(target, args); System.out.println("返回值:一堆美女" ); return invoke; } } public class MainTest { public static void main (String[] args) { ManTikTok leiTikTok = new LvTikTok(); ManTikTok proxy = JdkTiktokProxy.getProxy(leiTikTok); proxy.tiktok(); ((SellTikTok)proxy).sell(); System.out.println(Arrays.asList(proxy.getClass().getInterfaces())); } }
动态代理之Cglib 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class LvTikTok { public void tiktokHaha () { System.out.println("吕小医,tiktok.... haha...." ); } } public class CglibTest { public static void main (String[] args) { LvTikTok tikTok = new LvTikTok(); LvTikTok proxy = CglibProxy.createProxy(tikTok); proxy.tiktokHaha(); } } public class CglibProxy { public static <T> T createProxy (T t) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(t.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept (Object obj, Method method, //为了能获取到原方法的一些元数据信息 Object[] args, MethodProxy proxy) throws Throwable { System.out.println("cglib上场le .......xxx" ); Object invoke = proxy.invokeSuper(obj,args); return invoke; } }); Object o = enhancer.create(); return (T) o; } }
组合模式 组合多个对象形成树形结构以表示有整体-部分关系层次结构,组合模式可以让客户端统一对待单个对象和组合对象
把一组相似的对象当作一个单一的对象。如:树形菜单
component (抽象构件:容器):它可以是接口或者抽象类,为叶子构建和子容器构建对象声明接口,在该角色中可以包含所有子类共有的行为的实现和声明。在抽象构建中定义了访问及管理它的子构件的方法,如增加子构件,删除子构件,获取子构件等。
leaf(叶子构建):叶子构建可以说就是各种类型的文件!叶子构建没有子构件。它实现了抽象构建中的定义的行为。对于那些访问子容器,删除子容器,增加子容器的就报错。
compsite(子容器构建):它在组合模式中表示容器节点对象,容器结点是子节点,可以是子容器,也可以是叶子构建,它提供一个集合来存储子节点。
应用场景
在具有整体和部分的层次结构种希望通过一种忽略整体与个体之间差异的,客户端一致对待的情况。
在一个使用面向对象语言开发的系统中需要处理一个树形结构的。
在一个系统中能分离出叶子和容器的,而且他们的类型还固定不变,需要增加一些新的类型……
优缺点 优点:
可以清楚地定义分层次的复杂类型,表示对象的全部层次或者部分层次 ,它让客户端忽略了层次的差异,方便对整个层次经行控制。
客户端可以一致的使用一个组合模式或对单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端的代码。
在组合模式种增加新的容器构件和叶子构件都很方便,无需对现有类库进行任何修改,符合开闭原则。
为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合可以形成复杂的树形机构,但对树形结构的控制却很简单。
缺点: 在增加新的构件时就比较难咯。而且难以限定,有时候希望在一个容器种能有某些特定的对象,例如在某个文件夹只能有image或者gif等。这个就比较难以实现。
实现 杀毒软件,该软件能够对某个文件夹杀毒,也可以指定对某些文件杀毒。
AbstractFile: 抽象文件类,充当抽象构建。
1 2 3 4 5 6 7 public abstract class AbstractFiles { public abstract void add (AbstractFiles af) ; public abstract void remove (AbstractFiles af) ; public abstract AbstractFiles get (int i) ; public abstract void killVirus () ; }
叶子节点:文件类型,就写了一种。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class ImageFile extends AbstractFiles { private String name; public ImageFile (String name) { this .name=name; } @Override public void add (AbstractFiles af) { System.out.println("不支持该方法" ); } @Override public void remove (AbstractFiles af) { System.out.println("不支持该方法" ); } @Override public AbstractFiles get (int i) { System.out.println("不支持该方法" ); return null ; } @Override public void killVirus () { System.out.println("开始进行--" +name+"--文件杀毒" ); } }
文件类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Folder extends AbstractFiles { private ArrayList<AbstractFiles> list = new ArrayList<AbstractFiles>(); private String name; public Folder (String name) { this .name=name; } @Override public void add (AbstractFiles af) { list.add(af); System.out.println("添加成功" ); } @Override public void remove (AbstractFiles af) { if (list.remove(af)) { System.out.println("删除成功" ); }else { System.out.println("删除失败" ); } } @Override public AbstractFiles get (int i) { return list.get(i); } @Override public void killVirus () { System.out.println("对文件夹" +name+"进行杀毒" ); for (Object o:list) { ((AbstractFiles)o).killVirus(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Client { public static void main (String[] args) { AbstractFiles f1 = new Folder("主文件夹" ); AbstractFiles file1= new ImageFile("孙悟空.png" ); AbstractFiles file2= new ImageFile("龙珠.jpg" ); AbstractFiles file3= new ImageFile("帅哥威.gif" ); f1.add(file1); f1.add(file2); f1.add(file3); f1.killVirus(); file1.killVirus(); } }
1 2 3 4 5 6 7 8 添加成功 添加成功 添加成功 对文件夹主文件夹进行杀毒 开始进行--孙悟空.png--文件杀毒 开始进行--龙珠.jpg--文件杀毒 开始进行--帅哥威.gif--文件杀毒 开始进行--孙悟空.png--文件杀毒
外观模式 是什么 外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式
Facade(外观角色):这是核心类,在客户端可以调用他的方法,在外观角色中可以知道相关的子系统的功能和责任;在正常情况下,他将所有从客户端的请求委派到各个相应的子系统,传递给相应的子系统对象处理
SubSystem(子系统角色):在软件系统中可以有一个或多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用。对于子系统而言,外观类只是另一个客户。
优缺点 优点:
它对客户端屏蔽了子系统的组件(可以直接调用外观模式),减少了系统的耦合度,使客户端所需要调用的所处理的类的数目减少,全让外观模式,小二去解决。简化客户端代码!
它实现了系统与客户端之间的松耦合关系,子系统的修改不会影响客户端代码,只要修改外观模式就好。
一个子系统的修改该对其它子系统没有任何影响,而且子系统内部变化也不会影响到外观模式。
缺点:
并不能限制客户端直接调用子系统,如果客户端对子系统类做太多限制则减少了可变形和灵活性。
如果设计不当,增加新的子系统就可能修改外观类的源代码,不符合开闭原则。
应用场景 1、当客户端需要调用一系列复杂子系统时候,可以提供一个简单入口的时候使用外观模式。
2、客户端与子系统有很大的耦合性,使用外观模式可以充分解耦。
3、再层次化结构中可以使用外观模式的定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,减低层与层之间的耦合。JAVA 的三层开发模式。
分布式系统的网关
Tomcat源码中的RequestFacade干什么的?
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class Edu { public void assignSchool (String name) { System.out.println(name+",你的孩子明天去 硅谷大学附属幼儿园 报道......" ); } } public class Police { public void resgister (String name) { System.out.println(name + "已办理落户" ); } } public class Social { public void handleSocial (String name) { System.out.println(name+",你的社保关系已经转移...." ); } } public class WeiXinFacade { Police police = new Police(); Edu edu = new Edu(); Social social = new Social(); public void handleXxx (String name) { police.resgister(name); edu.assignSchool(name); social.handleSocial(name); } public void resgister (String name) { police.resgister(name); } public void assignSchool (String name) { edu.assignSchool(name); } } public class MainTest { public static void main (String[] args) { WeiXinFacade facade = new WeiXinFacade(); facade.handleXxx("吕小医" ); } }
享元模式
享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。对象结构型
在享元模式中可以共享的相同内容称为内部状态(IntrinsicState),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
享元模式包含如下角色:
Flyweight (享元抽象类):一般是接口或者抽象类,定义了享元类的公共方法。这些方法可以分享内部状态的数据,也可以调用这些方法修改外部状态。
ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类的方法,为享元对象开辟了内存空间来保存享元对象的内部数据,同时可以通过和单例模式结合只创建一个享元对象。
UnshareConcreteFlyweight(非共具体享元类):并不是所有的享元类都需要被共享的有的享元类就不要被共享,可以通过享元类来实例一个非共享享元对象。
Flyweight(享元工厂类):享元工厂类创建并且管理享元类,享元工厂类针对享元类来进行编程,通过提供一个享元池来进行享元对象的管理。一般享元池设计成键值对,或者其他的存储结构来存储。当客户端进行享元对象的请求时,如果享元池中有对应的享元对象则直接返回对应的对象,否则工厂类创建对应的享元对象并保存到享元池。
享元模式和原型模式:原型模式是返回的克隆,而享元模式返回的是本身,享元模式存在的意义是共享一个实体,怎么保证有序的拿到实体
优缺点 优点:
节约系统的开销。保证一个常用的对象只有一个!
外部状态不会影响内部状态,可以在不同环境下进行共享哦。
缺点:
享元模式使逻辑变得更加复杂,需要将享元对象分出内部状态和外部状态。
并且为了使对象可以共享,外部状态在很多情况下是必须有的,比如围棋的位置。当读取外部状态时明显会增加运行时间。
应用场景
一个系统有大量细粒度化的对象,占据大量的内存。
对象大部分属性可以外部化,并且能将外部的属性放入内部属性中来。
使用享元模式需要维护享元池,所以要用那种常用的经常调用的对象可以使用享元模式。
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 public abstract class AbstractWaitressFlyweight { boolean canService = true ; abstract void service () ; abstract void end () ; public boolean isCanService () { return canService; } } import lombok.AllArgsConstructor;@AllArgsConstructor public class BeautifulWaitress extends AbstractWaitressFlyweight { String id; String name; int age; @Override void service () { System.out.println("工号:" +id+";" +name+" " +age+" 正在为您服务..." ); this .canService = false ; } @Override void end () { System.out.println("工号:" +id+";" +name+" " +age+" 服务结束...请给五星好评" ); this .canService = true ; } } public class ZuDao { private static Map<String,AbstractWaitressFlyweight> pool = new HashMap<>(); static { BeautifulWaitress waitress = new BeautifulWaitress("1111" ,"张三" ,18 ); BeautifulWaitress waitress2 = new BeautifulWaitress("9527" ,"李四" ,20 ); pool.put(waitress.id,waitress); pool.put(waitress2.id,waitress2); } public void addWaitress (AbstractWaitressFlyweight waitressFlyweight) { pool.put(UUID.randomUUID().toString(),waitressFlyweight); } public static AbstractWaitressFlyweight getWaitress (String name) { AbstractWaitressFlyweight flyweight = pool.get(name); if (flyweight == null ){ for (AbstractWaitressFlyweight value : pool.values()) { if (value.isCanService()){ return value; } }; return null ; } return flyweight; } } public class MainTest { public static void main (String[] args) { AbstractWaitressFlyweight waitress = ZuDao.getWaitress("" ); waitress.service(); System.out.println(waitress); AbstractWaitressFlyweight waitress1 = ZuDao.getWaitress("" ); waitress1.service(); System.out.println(waitress1); waitress1.end(); AbstractWaitressFlyweight waitress2 = ZuDao.getWaitress("" ); System.out.println(waitress2); } }
行为型模式-11 (Behavioral Patterns)
概述
行为型模式关注点“==怎样运行对象/类==”所以我们关注下类/对象的运行时流程控制
行为型模式用于描述程序在运行时复杂的流程控制,
描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
模板方法(Template Method)模式:父类定义算法骨架,某些实现放在子类
策略(Strategy)模式:每种算法独立封装,根据不同情况使用不同算法策略
状态(State)模式:每种状态独立封装,不同状态内部封装了不同行为
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
职责链(Chain of Responsibility)模式:所有处理者封装为链式结构,依次调用
备忘录(Memento)模式:把核心信息抽取出来,可以进行保存
解释器(Interpreter)模式:定义语法解析规则
观察者(Observer)模式:维护多个观察者依赖,状态变化通知所有观察者
中介者(Mediator)模式:取消类/对象的直接调用关系,使用中介者维护
迭代器(Iterator)模式:定义集合数据的遍历规则
访问者(Visitor)模式:分离对象结构,与元素的执行算法
除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式
模板方法 在模板模式(Template Pattern)中,==一个抽象类公开定义了执行它的方法的方式模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行==。
模式中的角色:
抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。
优缺点 优点 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点 每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
适用场景 在某些类的算法中,用了相同的方法,造成代码的重复。
控制子类扩展,子类必须遵守算法规则。
Spring的整个继承体系都基本用到模板方法;
BeanFactory.getBean(1,2,3,4)–A1—A2—A3—A4(全部被完成).
JdbcTemplate、RedisTemplate都允许我们再扩展…..
我们自己的系统也应该使用模板方法组织类结构 .
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package top.lvxiaoyi.behavioral.template;public abstract class CookTemplate { public void cook () { heating(); addfood(); addsalt(); stirfry(); end(); } public void heating () { System.out.println("开火..." ); }; public abstract void addfood () ; public abstract void addsalt () ; public void stirfry () { System.out.println("翻炒中..." ); }; public void end () { System.out.println("出锅...." ); }; } public class AutoCookMachine extends CookTemplate { @Override public void addfood () { System.out.println("放了三个小白菜" ); } @Override public void addsalt () { System.out.println("放了三勺盐" ); } } public class MainTest { public static void main (String[] args) { AutoCookMachine cookMachine = new AutoCookMachine(); cookMachine.cook(); } }
策略模式 策略(Strategy)模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。属于对象行为模式。
策略模式的主要角色如下:
抽象策略(Strategy)类:公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
优缺点 优点 1、可以动态的改变对象的行为
缺点 1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类
2、策略模式将造成产生很多策略类
应用场景 使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句 什么是Spring的 InstantiationStrategy 线程池拒绝策略
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public interface GameStrategy { void warStrategy () ; } public class RandomStrategy implements GameStrategy { @Override public void warStrategy () { System.out.println("大乱斗..." ); } } public class SteadyStrategy implements GameStrategy { @Override public void warStrategy () { System.out.println("各路小心...及时支援..." ); } } public class UziStrategy implements GameStrategy { @Override public void warStrategy () { System.out.println("uzi....." ); } } public class TeamGNR { private GameStrategy gameStrategy; public void setGameStrategy (GameStrategy gameStrategy) { this .gameStrategy = gameStrategy; } public void startGame () { System.out.println("游戏开始....." ); gameStrategy.warStrategy(); System.out.println("win......" ); } } public class MainTest { public static void main (String[] args) { TeamGNR gnr = new TeamGNR(); gnr.setGameStrategy(new RandomStrategy()); gnr.startGame(); } }
状态模式 状态(State)模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式包含以下主要角色:
环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
总结: 使用状态模式前,客户端外界需要介入改变状态,而状态改变的实现是琐碎或复杂的。 使用状态模式后,客户端外界可以直接使用事件Event实现,根本不必关心该事件导致如何状态变化,这些是由状态机等内部实现。 这是一种Event-condition-State,状态模式封装了condition-State部分。 每个状态形成一个子类,每个状态只关心它的下一个可能状态,从而无形中形成了状态转换的规则。如果新的状态加入,只涉及它的前一个状态修改和定义。
优缺点 优点 在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;
缺点 缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。
应用场景 状态模式在工作流或游戏等各种系统中有大量使用,甚至是这些系统的核心功能设计,例如ERP系统中,一个批文的状态有多种:未办;正在办理;正在批示;正在审核;已经完成等各种状态,使用状态机可以封装这个状态的变化规则,从而达到扩充状态时,不必涉及到状态的使用者。
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public interface TeamState { void playGame () ; TeamState next () ; } public class BeafNodleState implements TeamState { @Override public void playGame () { System.out.println("饱饱的一顿牛肉面......中了诅咒,输了" ); } @Override public TeamState next () { return new MatchState(); } } public class MatchState implements TeamState { @Override public void playGame () { System.out.println("全力以赴打比赛...." ); } @Override public TeamState next () { return new VocationState(); } } public class SKTTeam { private TeamState teamState; public void setTeamState (TeamState teamState) { this .teamState = teamState; } public void startGame () { teamState.playGame(); } void nextState () { teamState = teamState.next(); } } public class MainTest { public static void main (String[] args) { SKTTeam sktTeam = new SKTTeam(); TeamState state = new VocationState(); sktTeam.setTeamState(state); sktTeam.startGame(); state = state.next(); sktTeam.setTeamState(state); sktTeam.startGame(); state = state.next(); sktTeam.setTeamState(state); sktTeam.startGame(); } }
中介者模式 中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,减少对象间混乱的依赖关系,从而使其耦合松散,而且可以独立地改变它们之间的交互。对象行为型模式。
Mediator: 抽象中介者
ConcreteMediator: 具体中介者
Colleague: 抽象同事类
ConcreteColleague: 具体同事类
优缺点 应用场景
当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
SpringMVC 的 DispatcherServlet是一个中介者,他会提取Controller、Model、View来进行调用。而无需controller直接调用view之类的渲染方法
分布式系统中的网关
迪米特法则的一个典型应用
中介者和外观(门面)模式区别?
中介者双向操作,门面偏向于封装某一方
…….
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 public abstract class Captain { abstract void fly () ; abstract void land () ; abstract void success () ; } public class ControlTower { private boolean canDo = true ; public void acceptRequest (Captain captain,String action) { if ("fly" .equals(action)||"land" .equals(action)){ if (canDo == true ){ System.out.println("允许......" ); canDo = false ; }else { System.out.println("不允许....." ); } } if ("success" .equals(action)){ canDo = true ; } } } public class HU8778 extends Captain { ControlTower controlTower; public void setControlTower (ControlTower controlTower) { this .controlTower = controlTower; } @Override void fly () { System.out.println("HU8778请求起飞......" ); controlTower.acceptRequest(this ,"fly" ); } @Override void land () { System.out.println("HU8778请求降落......" ); controlTower.acceptRequest(this ,"land" ); } @Override void success () { System.out.println("完成......" ); controlTower.acceptRequest(this ,"success" ); } } public class SC8633 extends Captain { ControlTower controlTower ; public void setControlTower (ControlTower controlTower) { this .controlTower = controlTower; } @Override void fly () { System.out.println("SC8633 请求起飞......" ); controlTower.acceptRequest(this ,"fly" ); } @Override void land () { System.out.println("SC8633 请求降落......" ); controlTower.acceptRequest(this ,";land" ); } @Override void success () { System.out.println("SC8633 完成......" ); controlTower.acceptRequest(this ,"fly" ); } } public class XC9527 extends Captain { @Override void fly () { System.out.println("XC9527请求起飞...." ); } @Override void land () { System.out.println("XC9527请求降落...." ); } @Override void success () { System.out.println("XC9527请求降落...." ); } }
观察者模式 观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。对象行为型模式
Subject: 目标 ConcreteSubject: 具体目标 Observer: 观察者 ConcreteObserver: 具体观察者
优缺点 优点
观察者和被观察者是抽象耦合的。
建立一套触发机制。
缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
应用场景
当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
Spring事件机制如何实现?
Vue的双向绑定核心
响应式编程核心思想
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public abstract class AbstractFans { List<AbstractTikToker> tikTokers; abstract void acceptMsg (String msg) ; void follow (AbstractTikToker tikToker) { tikToker.addFans(this ); }; } public abstract class AbstractTikToker { abstract void addFans (AbstractFans fans) ; abstract void notifyFans (String msg) ; } public class HumanFans extends AbstractFans { @Override void acceptMsg (String msg) { System.out.println("主播说:" +msg); } } public class MMTikToker extends AbstractTikToker { List<AbstractFans> fansList = new ArrayList<>(); void startSell () { System.out.println("雷丰阳... 开始卖货...源码设计课" ); notifyFans("我开始卖东西了,是源码设计课,只要666" ); } void endSell () { System.out.println("雷丰阳... 结束卖货...源码设计课" ); notifyFans("课已经卖完了,记得五星好评..." ); } @Override void addFans (AbstractFans fans) { fansList.add(fans); } @Override void notifyFans (String msg) { for (AbstractFans fans : fansList) { fans.acceptMsg(msg); } } }public class RobotFans extends AbstractFans { @Override void acceptMsg (String msg) { System.out.println("呸...." ); } }
备忘录模式 备忘录(Memento)模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。对象行为型模式
发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
优缺点 优点
缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
应用场景
保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
例如:
session的活化钝化;
浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面
数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中
编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做
虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子
Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset –hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻
棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 public class GameServer { Map<Integer,GameRecord> records = new HashMap<>(); int i = 1 ; void add (GameRecord gameRecord) { gameRecord.setId(i++); records.put(gameRecord.id,gameRecord); } LvGamer getRecord (Integer id) throws Exception { GameRecord gameRecord = records.get(id); LvGamer lvGamer = new LvGamer(); BeanUtils.copyProperties(lvGamer,gameRecord); return lvGamer; } } @Data public class GameRecord { Integer id; Integer coin; Integer hp; Integer mp; Integer level; void getCurrent () { System.out.println("coin:" +coin+";\t" +"hp:" +hp+";\t mp:" +mp); } } @Data public class LvGamer { Integer coin; Integer hp; Integer mp; Integer level; GameServer gameServer = new GameServer(); void saveGameRecord () throws Exception { System.out.println("正在保存当前记录...." ); GameRecord gameRecord = new GameRecord(); BeanUtils.copyProperties(gameRecord,this ); gameServer.add(gameRecord); } LvGamer getFromMemento (Integer id) throws Exception { System.out.println("获取历史存档信息...." ); LvGamer record = gameServer.getRecord(id); return record; } void playGame () { int i = new Random().nextInt(); System.out.println("......(〃'▽'〃)......" +i); coin = i; hp = i; mp = i; level = i; } void exitGame () throws Exception { System.out.println("退出&存档" ); saveGameRecord(); } } public class MainTest { public static void main (String[] args) throws Exception { LvGamer lvGamer = new LvGamer(); lvGamer.playGame(); lvGamer.saveGameRecord(); lvGamer.playGame(); lvGamer.playGame(); lvGamer.saveGameRecord(); LvGamer fromMemento = lvGamer.getFromMemento(1 ); fromMemento.playGame(); } }
解释器模式 解释器(Interpreter)模式:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。类行为型模式
抽象表达式(Abstract Expression)角色:
定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
终结符表达式(Terminal Expression)角色:
是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
非终结符表达式(Nonterminal Expression)角色:
也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
环境(Context)角色:
通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端(Client):
主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
优缺点 优点
易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 “开闭原则”。
缺点
对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
应用场景
可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
一些重复出现的问题可以用一种简单的语言来进行表达。
一个语言的文法较为简单。
对执行效率要求不高。
例如:
Spring EL表达式中的解释器模式
Thymeleaf等模板引擎的语法解析
编译原理
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 public abstract class IDCardExpression { abstract boolean interpret (String expression) ; } public class TerminalExpression extends IDCardExpression { IDCardExpression childExp; Set<String> data; String symbol; public TerminalExpression ( Set<String> data,String symbol) { this .data = data; this .symbol = symbol; } @Override boolean interpret (String expression) { String[] split = expression.split(symbol); boolean result = false ; for (String s : split) { if (data.contains(s)){ return true ; }; } return false ; } } public class OrExpression extends IDCardExpression { private IDCardExpression cityExp; private IDCardExpression typeExp; public OrExpression (IDCardExpression cityExp, IDCardExpression typeExp) { this .cityExp = cityExp; this .typeExp = typeExp; } @Override boolean interpret (String expression) { return cityExp.interpret(expression) || typeExp.interpret(expression); } } public class Area { Set<String> city = new HashSet<>(); Set<String> type = new HashSet<>(); IDCardExpression idCardReader; public Area () { city.add("武汉市" ); city.add("上海市" ); type.add("医生" ); type.add("儿童" ); TerminalExpression city = new TerminalExpression(this .city, ":" ); TerminalExpression type = new TerminalExpression(this .type, "-" ); idCardReader = new OrExpression(city,type); } void getTicket (String expression) { boolean interpret = idCardReader.interpret(expression); if (interpret){ System.out.println("恭喜你,免票通过...." ); }else { System.out.println("对不起,请2元购票...." ); } } } public class MainTest { public static void main (String[] args) { Area area = new Area(); String sr = "吕小医" ; area.getTicket(sr); } }
命令模式 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
命令模式包含以下主要角色:
抽象命令类(Command)角色:
声明执行命令的接口,拥有执行命令的抽象方法 execute()。
具体命令类(Concrete Command)角色:
是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:
执行命令功能的相关操作,是具体命令对象业务的真正实现者。
调用者/请求者(Invoker)角色:
是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
优缺点 优点
类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command 抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。
可扩展性:Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严 重的代码耦合。
命令模式结合其他模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少 Command子类的膨胀问题。
缺点 命令模式也是有缺点的,请看Command的子类:如果有N个命令,问题就出来 了,Command的子类就可不是几个,而是N个,这个类膨胀得非常大,这个就需要读者在项 目中慎重考虑使用。
应用场景 对于大多数请求-响应模式的功能,比较适合使用命令模式,正如命令模式定义说的那样,命令模式对实现记录日志、撤销操作等功能比较方便。
mvc就是典型的命令模式。
当系统需要执行一组操作时,命令模式可以定义宏命令(一个命令组合了多个命令)来实现该功能。
结合备忘录模式还可以实现命令的撤销和恢复。
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public interface Command { void execute () ; } public class LvReceiver { public void online () { System.out.println("leifengyang 在线上直播大厂课......" ); } public void travel () { System.out.println("leifengyang 出差....." ); } } public class OnlineCommand implements Command { private LvReceiver receiver = new LvReceiver(); @Override public void execute () { System.out.println("要去上(吹)课(牛)...." ); receiver.online(); } } public class WuHanTravelCommand implements Command { private LvReceiver receiver = new LvReceiver(); @Override public void execute () { System.out.println("我要出(旅)差(游)...." ); receiver.travel(); } } public class TeacherTongInvoker { Command command; public void call () { command.execute(); } public void setCommand (Command command) { this .command = command; } } public class MainTest { public static void main (String[] args) { TeacherTongInvoker invoker = new TeacherTongInvoker(); invoker.setCommand(new OnlineCommand()); invoker.call(); } }
迭代器模式 迭代器(Iterator)模式:提供一个对象(迭代器)来顺序访问聚合对象(迭代数据)中的一系列数据,而不暴露聚合对象的内部表示。对象行为型模式
抽象聚合(Aggregate)角色:
定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
具体聚合(ConcreteAggregate)角色:
抽象迭代器(Iterator)角色:
定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
具体迭代器(Concretelterator)角色:
实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
优缺点 优点
它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足 “开闭原则” 的要求。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
应用场景
访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
需要为一个聚合对象提供多种遍历方式。
为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 public abstract class BeautifulMan { private List<String> girlFriends = new ArrayList<>(); void likeYou (String name) { girlFriends.add(name); }; void sayBye (String name) { girlFriends.remove(name); }; public Itr getIterator () { return new Iterator(); } class Iterator implements Itr { private int cursor = 0 ; @Override public boolean hasNext () { return cursor < girlFriends.size(); } @Override public String next () { String s = girlFriends.get(cursor); cursor++; return s; } @Override public String firstLove () { return girlFriends.get(0 ); } @Override public String current () { return girlFriends.get(girlFriends.size()-1 ); } } interface Itr { boolean hasNext () ; String next () ; String firstLove () ; String current () ; } } public class MaYuCheng extends BeautifulMan {} public class MainTest { public static void main (String[] args) { MaYuCheng cheng = new MaYuCheng(); cheng.likeYou("王刚" ); cheng.likeYou("李强" ); cheng.likeYou("赵根" ); BeautifulMan.Itr itr = cheng.getIterator(); String s = itr.firstLove(); System.out.println(s); String current = itr.current(); System.out.println(current); System.out.println("=================" ); while (itr.hasNext()){ String next = itr.next(); System.out.println(next); } } }
总结 创建型模式
==单例模式==:保证一个对象
原型模式:快速克隆一个自己
==工厂方法模式:==
简单工厂(一个方法):if-else判断创造对象(抽象父类传递)。产品线单一
工厂方法(很多方法):每个产品一个方法
==抽象工厂模式:==
==建造者模式:==每一个建造细节都可以传入,但是这些细节都不是必须的,如果是必须就是用构造器
构造型模式
==适配器模式:==连接两个不同的接口
桥接模式:连接同一个东西的两个不同纬度
==代理模式:==动态代理
==装饰模式:==静态代理=装饰。被装饰的对象传入。
==外观模式:==把多个类的方法,合成到一个类里面引用。
享元模式:将原对象通过池技术共享
==组合模式:==a里面属性有b对象就是组合
过滤器模式:
行为型模式
==模板方法模式:==父类定义一个大方法,里面调用一堆小方法步骤,小方法的步骤由子类实习
==策略模式:==每一种算法封装成一个策略。
命令模式:Controller、service、do
==责任链模式:==Fileter、维护链条+游标+递归
==状态模式:==和策略模式很像,状态要维护状态流转
==观察者模式:==把所有观察者list起来,然后foreach
中介者模式:所有类都引入一个类的对象,通过这一个对象调用
迭代器模式:自定义遍历算法。自己本类的迭代器遍历自己本类里面的集合
访问者模式:小爱机器人,cpu、disk要能够被升级包升级
备忘录模式:把复杂对象的关键属性抽取出来进行保存
解释器模式:语法分析,大解析器组合了很多小解释器,进行语法解析