创建型模式
模式 | 作用 |
---|---|
单例模式 | 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。 |
简单工厂模式 | 用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码) |
工厂方法模式 | 用来生产同一等级结构中的固定产品。(支持增加任意产品) |
抽象工厂模式 | 用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族) |
建造者模式 | 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。 |
原型模式 | 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式 |
一、单例模式(Single Pattern)
-
核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点(获取对象的方法)。
-
单例模式的优点
-
节少系统性能开销,程序执运行时可以永久驻留内存。
-
可以设置全局访问点,优化环共享资源访问,例如设计一个单例类,负责所有数据表的映射处理。
-
-
常见单例模式的实现方式
-
饿汉式(线程安全,调用效率高,不能延时加载)
-
懒汉式(线程安全,调用效率不高,可以延时加载)
-
双重检测锁模式double checked locking(由于JVM底层内部模型原因,会偶尔出现问题,java中不建议使用)
-
静态内部类式(线程安全,调用效率高,可以延时加载)
-
枚举式单例(线程安全,调用效率高,不能延时加载)
-
-
总结:不需要延时加载 枚举式好于饿汉式,需要延时加载 静态内部类好于懒汉式
饿汉式Demo
/**
* 饿汉式单例模式
*/
public class SingletonDemo01 {
//由于加载类时,类加载器天然的线程安全的机制 所以不需要使用synchronized同步块,所以效率高
private static SingletonDemo01 instance = new SingletonDemo01(); //类初始化时立即加载这个对象
private SingletonDemo01() {
}
public static SingletonDemo01 getInstance(){
return instance;
}
}
懒汉式Demo
/**
* 懒汉式单例模式
*/
public class SingletonDemo02 {
//类初始化时,不初始化这个对象,类创建时候延时加载
private static SingletonDemo02 instance;
private SingletonDemo02() {
}
//会出现线程不安全的问题 所以使用同步块,效率低
public static synchronized SingletonDemo02 getInstance(){
if (instance==null){
instance = new SingletonDemo02();
}
return instance;
}
}
双重检测锁Demo
package GOF23;
/**
* 双重检测锁模式
*/
public class SingletonDemo03 {
private static volatile SingletonDemo03 instance = null;
public SingletonDemo03() {
}
public static SingletonDemo03 getInstance() {
if (instance == null) {
SingletonDemo03 sc;
synchronized (SingletonDemo03.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo03.class) {
if (sc == null) {
sc = new SingletonDemo03();
}
}
instance = sc;
}
}
}
return instance;
}
}
静态内部类式Demo
/**
* 静态内部类实现单例模式
*/
public class SingletonDemo04 {
public SingletonDemo04() {
}
private static class SingletonClassInstance {
private static final SingletonDemo04 instance = new SingletonDemo04(); //final可加可不加
}
public static SingletonDemo04 getInstance() {
return SingletonClassInstance.instance;
}
}
枚举式Demo
/**
* 使用枚举实现单例模式 枚举本身就是通过jvm内部机制创建单例模式。有效的避免了通过反射、序列化的方式产生多例的问题
* 枚举实现单例无法实现延时加载
*
* wo枚举单例调用方式
* SingletonDemo05 instance = SingletonDemo05.INSTANCE;
* instance.singletonOperation();
*/
public enum SingletonDemo05 {
INSTANCE; //这个枚举元素,本身就是单例模式(没有延时加载)
// 添加自己需要的操作
public void singletonOperation() {
}
}
解决反射反序列化创建多个实例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
//普通创建方式
SingletonDemo02 s1 = SingletonDemo02.getInstance();
System.out.println(s1);
//通过反射方式创建单例模式对象
Class<SingletonDemo02> clazz = (Class<SingletonDemo02>) Class.forName("GOF23.SingletonDemo02");
Constructor<SingletonDemo02> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
SingletonDemo02 s3 = c.newInstance();
System.out.println(s3);
//通过反序列化方式构造多个单例对象
FileOutputStream fos = new FileOutputStream("d:/MyJava/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/MyJava/a.txt"));
SingletonDemo02 s4 = (SingletonDemo02) ois.readObject();
System.out.println(s4);
}
}
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 懒汉式单例模式 防止反序列化与反射创建多个实例解决方法
*/
public class SingletonDemo02 implements Serializable {
//类初始化时,不初始化这个对象,类创建时候延时加载
private static SingletonDemo02 instance;
private SingletonDemo02() {
if (instance != null) { //防止反射操作
throw new RuntimeException();
}
}
//会出现线程不安全的问题 所以使用同步块,效率低
public static synchronized SingletonDemo02 getInstance() {
if (instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
//反序列化时 会调用readRosolve() 方法,直接返回instance对象,而不再单子创建新对象,方式反序列化创建实例
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
性能测试代码
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
int threadNum = 10;
final CountDownLatch countDownlatch = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
for (int j = 0; j < 100000; j++) {
// Object o = SingletonDemo01.getInstance(); //53
// Object o = SingletonDemo02.getInstance(); //108
// Object o = SingletonDemo03.getInstance(); //57
// Object o = SingletonDemo04.getInstance(); //63
Object o = SingletonDemo05.INSTANCE; //50
}
countDownlatch.countDown();
}
).start();
}
countDownlatch.await(); //mian线程阻塞,直到计数器变为0,才会继续往下执行
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
二、工厂模式(Factory)
- 工厂模式实现了创建者与调用者分离。(为我们创建对象的模式)
- 分为三类:简单工程模式、工厂方法模式、抽象工厂模式
- 工厂模式的优点:符合面向对象的基本原则
- OPC(开闭原则,Open-closed Principle):一个软件的实体应当对扩展开放,对修改关闭。
- DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对现实编程。
- LoD(迪米特法则,Law of Demeter):值由于你直接朋友通信,而避免和陌生人通信。
- 核心本质:
- 实例化对象,用工厂方法代替new操作
- 讲选择实现类、创建对象统一管理和控制。从而将调用者跟我们实现类解耦
- 核心作用
- 简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
- 工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
- 抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
正常创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dn4JFYta-1580921151454)(C:\Users\CHEN\Desktop\多线程\Gof32-5.jpg)]
简单工厂模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GS0AM9fn-1580921151455)(C:\Users\CHEN\Desktop\多线程\Gof32-4.png)]
//Car 接口
public interface Car {
void run();
}
//Car 接口实现类
public class Audi implements Car{
@Override
public void run(){
System.out.println("Aodi run.");
}
}
/**
* 普通工厂(静态工厂)方式一
*/
public class CarFactory {
public static Car createCar(String type){
if ("audi".equals(type)) {
return new Audi();
}else if ("bmw".equals(type)){
return new Bmw();
}else {
return null;
}
}
}
/**
* 普通工厂(静态工厂)方式二
*/
public class CarFactory2 {
public static Car createAudi(){
return new Audi();
}
public static Car createBmw(){
return new Audi();
}
}
/**
* 简单工厂:虽然违背了面向对象的开闭原则,但是在实际开发中仍经常使用
*/
public class Client {
public static void main(String[] args) {
//普通对象创建对象
Car c1 = new Audi();
//简单(静态)工厂创建对象
Car c2 = CarFactory.createCar("audi");
Car c3 = CarFactory2.createAudi();
}
}
工厂方法模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2EtDfQJ-1580921151456)(C:\Users\CHEN\Desktop\多线程\Gof32-3.jpg)]
// car接口
public interface Car {
void run();
}
// Car接口实现类
public class Audi implements Car {
@Override
public void run(){
System.out.println("Aodi run.");
}
}
// Car工厂类接口
public interface CarFactory {
Car createCar();
}
// Car工厂类 实现
public class AudiFactory implements CarFactory {
@Override
public Car createCar() {
return new Audi();
}
}
/**
* 工厂方法模式:在需要扩展时只需要添加相应的工厂方法,不需要修改业务类代码
*/
public class Client {
public static void main(String[] args) {
Car c1 = new BmwFactory().createCar();
Car c2 = new AudiFactory().createCar();
c1.run();
c2.run();
}
}
抽象工厂模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KqMN02ZN-1580921151456)(C:\Users\CHEN\Desktop\多线程\Gof32-2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AAe6j1dp-1580921151457)(C:\Users\CHEN\Desktop\多线程\Gof32-1.png)]
// Engine 接口 与实现类(为保存代码定义到一个文件中,实际中单独定义。Seat与Tyre接口实现类同理。省略。)
public interface Engine {
void run();
void start();
}
class LuxuryEngine implements Engine{
@Override
public void run() {
System.out.println("Luxury Engine run.");
}
@Override
public void start() {
System.out.println("Luxury Engine start.");
}
}
class LowEngine implements Engine{
@Override
public void run() {
System.out.println("Low Engine run.");
}
@Override
public void start() {
System.out.println("Low Engine start.");
}
}
//Car 工厂接口
public interface CarFactory {
Engine createEngine();
Seat createSeat();
Tyre createTyre();
}
// Car工厂接口实现 (LowCarFactory代码同理。省略。)
public class LuxuryCarFactory implements CarFactory {
@Override
public Engine createEngine() {
return new LuxuryEngine();
}
@Override
public Seat createSeat() {
return new LuxurySeat();
}
@Override
public Tyre createTyre() {
return new LuxuryTyre();
}
}
package Factory.abstractfactory;
/**
* 抽象工厂模式,不可以增加产品,可以增加产品族
*/
public class Client {
public static void main(String[] args) {
CarFactory factory = new LowCarFactory();
Engine e = factory.createEngine();
e.run();
e.start();
Seat s = factory.createSeat();
s.massage();
Tyre t = factory.createTyre();
t.revolve();
}
}
三、建造者模式(Builder Pattern)
- 核心作用
- 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
- 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以构建出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
- 总结:当一个复杂对象由多个对象组成时,我们可以创建Builder类作为构建类来构建锁需要的子对象,再创建Director类作为生成这个复杂对象的装配类
代码实现
package Builder;
/**
* 通过模拟创建飞船 理解建造者模式
*/
public class AirShip {
private OrbitalModule orbitalModule;//轨道舱
private Engine engine; //发动机
private EscapeTower escapeTower; //逃逸塔
public OrbitalModule getOrbitalModule() {
return orbitalModule;
}
public void setOrbitalModule(OrbitalModule orbitalModule) {
this.orbitalModule = orbitalModule;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public EscapeTower getEscapeTower() {
return escapeTower;
}
public void setEscapeTower(EscapeTower escapeTower) {
this.escapeTower = escapeTower;
}
}
class OrbitalModule {
private String name;
public OrbitalModule(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Engine {
private String name;
public Engine(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class EscapeTower {
private String name;
public EscapeTower(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//创建构造器接口
public interface AirShipBuilder {
Engine builderEngine();
OrbitalModule orbitalModule();
EscapeTower builderEscapeTower();
}
//实现构造器结构
public class TestAirShipBuiler implements AirShipBuilder {
@Override
public Engine builderEngine() {
System.out.println("test发动机");
return new Engine("test发动机"); //此处可结合工厂模式获取对象
}
@Override
public OrbitalModule orbitalModule() {
System.out.println("test轨道舱");
return new OrbitalModule("test 轨道舱");
}
@Override
public EscapeTower builderEscapeTower() {
System.out.println("test逃逸塔");
return new EscapeTower("test 逃逸塔");
}
}
//创建装配器接口
public interface AirShipDirector {
/**
* 组装对象
*/
AirShip directAirShip();
}
//实现装配器接口 并使用构造器 装配对象
public class TestAirshipDirector implements AirShipDirector{
private AirShipBuilder builder;
public TestAirshipDirector(AirShipBuilder builder) {
this.builder = builder;
}
@Override
public AirShip directAirShip() {
Engine engine = builder.builderEngine();
EscapeTower escapeTower = builder.builderEscapeTower();
OrbitalModule orbitalModule = builder.orbitalModule();
AirShip ship = new AirShip();
ship.setEngine(engine);
ship.setEscapeTower(escapeTower);
ship.setOrbitalModule(orbitalModule);
return ship;
}
}
// 通过装配其获得对象
public class Client {
public static void main(String[] args) {
AirShipDirector director = new TestAirshipDirector(new TestAirShipBuiler());
AirShip ship = director.directAirShip();
}
}
四、原型模式 (Prototype)
-
核心作用:
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 就是java中的 克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
- 优势有:效率高(直接克隆,避免了重新执行构造过程步骤) 。
- 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。
-
原型模式实现
- Cloneable接口和clone方法
- Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了 clone()方法替我们做了绝大部分事情。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzBJolBa-1580921151457)(prototype.jpg)]
-
浅克隆存在的问题:被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
-
深克隆实现
- 深克隆把引用的变量指向复制过的新对象,而不是原有的被引用的对象。
- 深克隆:让已实现Clonable接口的类中的属性也实现Clonable接口
- 基本数据类型和String能够自动实现深度克隆(值的复制)
-
开发中的应用场景
- 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone 的方法创建一个对象,然后由工厂方法提供给调用者。
- spring中bean的创建实际就是两种:单例模式和原型模式。(当然,原型模式需要和工厂模式搭配起来)
克隆模式Demo
package prototype;
import java.util.Date;
/**
* 浅克隆- 克隆出不同的对象 但是对象中引用相同的引用
* Cloneable是标记接口 调用Object.clone方法 需要实现Cloneable接口才能使用clone方法
*/
public class Sheep implements Cloneable{
String name;
Date birthday;
public Sheep(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
return obj ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
package prototype;
import javax.xml.crypto.Data;
import java.util.Date;
/**
* 深克隆- 克隆出不同的对象 对象中的引用也不同
* Cloneable是标记接口 调用Object.clone方法 需要实现Cloneable接口才能使用clone方法
*/
public class Sheep2 implements Cloneable{
String name;
Date birthday;
public Sheep2(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Sheep2 s = (Sheep2)obj;
s.birthday = (Date) this.birthday.clone(); //深度克隆 克隆引用
return obj ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
package prototype;
import java.io.Serializable;
import java.util.Date;
/**
* 通过序列化反序列化方式实现深克隆
*/
public class Sheep3 implements Serializable {
String name;
Date birthday;
public Sheep3(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
package prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
public class Client {
public static void main(String[] args) throws Exception {
Date date = new Date(12345678901L);
//浅克隆 克隆出的对象的引用相同
Sheep s1 = new Sheep("多利",date);
Sheep s2 = (Sheep)s1.clone();
System.out.println("克隆对象:" + (s1==s2?"相同对象":"不同对象"));
System.out.println("克隆中对象date引用:" + (s1.getBirthday()==s2.getBirthday()?"相同对象":"不同对象"));
//深克隆 克隆出的对象的引用不同
Sheep2 s3 = new Sheep2("利多利",date);
Sheep2 s4 = (Sheep2) s3.clone();
System.out.println("克隆对象:" + (s3==s4?"相同对象":"不同对象"));
System.out.println("克隆中对象date引用:" + (s3.getBirthday()==s4.getBirthday()?"相同对象":"不同对象"));
//通过序列化反序列化实现深克隆
Sheep3 s5 = new Sheep3("少利",date);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s5);
byte[] bytes = bos.toByteArray(); //序列化到一个数字
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
Sheep3 s6 = (Sheep3)ois.readObject();
System.out.println("克隆对象:" + (s5==s6?"相同对象":"不同对象"));
System.out.println("克隆中对象date引用:" + (s5.getBirthday()==s6.getBirthday()?"相同对象":"不同对象"));
}
}
结构型模式
模式 | 作用 |
---|---|
代理模式 | 为真实对象提供一个代理,从而控制对真实对象的访问 |
适配模式 | 使原本由于接口不兼容不能一起工作的类可以一起工作 |
桥接模式 | 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。 |
组合模式 | 将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一的调用叶子对象和容器对象 |
装饰模式 | 动态地给一个对象添加额外的功能,比继承灵活 |
外观模式 | 为子系统提供统一的调用接口,使得子系统更加容易使用 |
享元模式 | 运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率 |
五、适配器模式(Adapter)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqsskOgC-1580921151458)(adapter.png)]
- 模式中的角色
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。 - 类适配器:通过继承Adaptee实现
- 对象适配器:通过传入Adaptee实现
适配器Demo
/**
* 被适配的类
*/
public class Adaptee {
public void request(){
System.out.println("被适配的对象方法执行了...");
}
}
public interface Target {
void handleReq();
}
/**
* 适配器 方式一 类适配器方式(继承),
*/
public class Adapter extends Adaptee implements Target {
@Override
public void handleReq() {
super.request();
}
}
/**
* 适配器 方式二 对象适配器(采用组合的方式)
*/
public class Adapter2 implements Target {
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleReq() {
adaptee.request();
}
}
public class Client {
public void test1(Target t){
t. handleReq();
}
public static void main(String[] args) {
Client client = new Client();
client.test1(new Adapter()); //类适配器调用
client.test1(new Adapter2(new Adaptee())); //对象适配器
}
}
六、代理模式(Proxy)
-
AOP(Aspect Orientend Programming面向切面编程)的核心机制就是代理模式;
-
代理模式核心作用:通过代理,控制对对象的访问,可以箱子控制访问某个类对象的方法,在这个方法前做前置或后置处理。即AOP的微观实现。
-
角色划分
- 抽象角色:定义代理角色和真是角色的公共对外方法。
- 真实角色:实现头像角色,定义真实角色所要实现的业务逻辑,提供代理角色调用。关注真的的业务逻辑
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并且附加自己的操作,将统一的流程控制放到代理角色中处理。
-
应用场景
- 安全代理:屏蔽对真实角色的直接访问
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要时候在加载真实对象。
-
分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)。JDK自带的动态代理、javaassist字节码操作库、CGLIB、ASM(底层使用指令,可维护性差)
-
动态代理相比于静态代理的优点
抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。 -
JDK自带的动态代理
- java.lang.reflect.Proxy
作用:动态生成代理类和对象 - java.lang.reflect.InvocationHandler(处理器接口)
可以通过invoke方法实现对真实角色的代理访问。
每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
静态代理Demo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IoNfXjMS-1580921151458)(proxy.png)]
public interface Star {
void confer(); //面谈
void signContract(); //签合同
void bookTiket(); //订票
void sing(); //表演唱歌
void coolectMoney(); //收钱
}
/**
* 被代理的类
*/
public class RealStar implements Star {
@Override
public void confer() {
System.out.println("RealStar.confer");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract");
}
@Override
public void bookTiket() {
System.out.println("RealStar.bookTiket");
}
@Override
public void sing() {
System.out.println("RealStar.sing");
}
@Override
public void coolectMoney() {
System.out.println("RealStar.coolectMoney");
}
}
/**
* 静态代理类
*/
public class ProxyStar implements Star {
private Star star ;
public ProxyStar(Star star) {
this.star = star;
}
@Override
public void confer() {
System.out.println("ProxyStar.confer");
}
@Override
public void signContract() {
System.out.println("ProxyStar.signContract");
}
@Override
public void bookTiket() {
System.out.println("ProxyStar.bookTiket");
}
@Override
public void sing() {
star.sing();
}
@Override
public void coolectMoney() {
System.out.println("ProxyStar.coolectMoney");
}
}
public class Client {
public static void main(String[] args) {
Star real = new RealStar();
Star proxy = new ProxyStar(real);
proxy.confer();
proxy.signContract();
proxy.bookTiket();
proxy.sing(); //调用静态代理的sing方法实际是调用了被代理类的sing方法
proxy.coolectMoney();
}
}
JDK自带动态代理Demo
public interface Star {
void confer(); //面谈
void signContract(); //签合同
void bookTiket(); //订票
void sing(); //表演唱歌
void coolectMoney(); //收钱
}
/**
* 被代理的类
*/
public class RealStar implements Star {
@Override
public void confer() {
System.out.println("RealStar.confer");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract");
}
@Override
public void bookTiket() {
System.out.println("RealStar.bookTiket");
}
@Override
public void sing() {
System.out.println("RealStar.sing");
}
@Override
public void coolectMoney() {
System.out.println("RealStar.coolectMoney");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 定义处理器,实现InvocationHandler接口
*/
public class StarHandler implements InvocationHandler {
Star realStar; //将被代理的对象传入
public StarHandler(Star realStar) {
this.realStar = realStar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前执行了");
//使用反射调用被代理对象的方法
method.invoke(realStar,args);
System.out.println("代理后执行了");
return null;
}
}
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar); //生成代理处理器
//创建代理对象 传入类加载器,接口,代理处理器
Star proxy =(Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{Star.class}, handler);
proxy.bookTiket();
}
}
七、桥接模式(Bridge)
- 桥接模式
- 桥接模式可以取代多层继承的方案。 多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
- 桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
总结:当一个产品有多维度的分类(需要多继承),通过继承体现会变得很复杂。当维度发生变化时候,维护变得复杂,这时候我们可以使用桥接模式(通过组合的方式来体现维度与产品的关系)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJQEEYoF-1580921151458)(birdge.png)]
传统继承模式Demo
public interface Computer {
void sale();
}
class Desktop implements Computer{
@Override
public void sale() {
System.out.println("销售台式机");
}
}
class Laptop implements Computer{
@Override
public void sale() {
System.out.println("销售笔记本");
}
}
class Pad implements Computer{
@Override
public void sale() {
System.out.println("销售平板");
}
}
class LenovoDesktop extends Desktop{
@Override
public void sale() {
System.out.println("销售联想台式机");
}
}
桥接模式Demo
/**
* 创建品牌维度
*/
public interface Brand {
void sale();
}
class Lenovo implements Brand{
@Override
public void sale() {
System.out.println("销售联想电脑");
}
}
class Dell implements Brand{
@Override
public void sale() {
System.out.println("销售戴尔电脑");
}
}
/**
* 创建产品维度 组合品牌维度
*/
public abstract class Computer2 {
protected Brand brand;
public Computer2(Brand brand) {
this.brand = brand;
}
public void sale(){
brand.sale(); //调用品牌维度方法
}
}
class Desktop2 extends Computer2{
public Desktop2(Brand brand) {
super(brand);
}
@Override
public void sale() {
super.sale();
System.out.println("销售台式机");
}
}
class Laptop2 extends Computer2{
public Laptop2(Brand brand) {
super(brand);
}
@Override
public void sale() {
super.sale();
System.out.println("销售笔记本");
}
}
public class clinet {
public static void main(String[] args) {
Computer2 c = new Laptop2(new Dell());
c.sale();
}
}
八、组合模式(Composite)
-
组合模式和组合不同。组合模式就是用树形结构组合对象。
-
把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象
-
组合模式核心:
- 抽象构件(Component):定义了叶子和容器构建的共同点
- 叶子(Leaf):无子节点
- 容器(Composite):有容器特征,可以包含子节点
-
组合模式工作流程分析:
-
组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子。
-
当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。其中,使用了递归调用的机制对整个结构进行处理。
-
-
Junit单元测试框架底层设计 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJfHiHEk-1580921151459)(composite.jpg)]
组合模式定义
public interface Component {
void operation();
}
interface Leaf extends Component {
}
interface Composite extends Component {
void add(Component c);
void reMove(Component c);
Component getChild(int index);
}
组合模式案例Demo
package composite;
import java.util.ArrayList;
import java.util.List;
/**
* 模拟杀毒软件
*/
public interface AbstractFile {
void killVirus(); //抽象构件中,杀毒方法
}
//相当于叶子
class ImageFile implements AbstractFile{
private String name;
public ImageFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("图片文件:"+name+"进行查杀!");
}
}
//相当于叶子
class VideoFile implements AbstractFile{
private String name;
public VideoFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("视频文件:"+name+"进行查杀!");
}
}
//相当于叶子
class TextFile implements AbstractFile{
private String name;
public TextFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("文本文件:"+name+"进行查杀!");
}
}
//文件夹查杀相当于容器组件
class Folder implements AbstractFile{
private String name;
//定义容,器存放本容器的子节点
private List<AbstractFile> list = new ArrayList<AbstractFile>();
public Folder(String name) {
this.name = name;
}
public void add(AbstractFile File){
list.add(File);
}
public void remove(AbstractFile File) {
list.remove(File);
}
public AbstractFile getchild(int index) {
return list.get(index);
}
@Override
public void killVirus() {
System.out.println("文件夹"+name+"进行查杀");
for (AbstractFile file : list) {
file.killVirus(); //若为文件夹。形成递归
}
}
}
public class client {
public static void main(String[] args) {
AbstractFile f2,f3,f5,f6;
Folder f1= new Folder("文件夹1");
f2= new ImageFile("a.jpg");
f3= new TextFile("hello.txt");
Folder f4= new Folder("文件夹2");
f4.add(f2);
f4.add(f3);
f1.add(f2);
f1.add(f3);
f1.add(f4);
f1.killVirus();
}
}
九、装饰模式(Decorator、Wrapper)
-
动态的为一个对象增加新功能
-
装饰模式是一种用于代理继承的技术,无需通过继承添加子类就能扩展对象的新功能。使用对象的关联关系替代继承关系,更加灵活,同时避免类型体系的快速膨胀。
-
实现细节:
- Component抽象构件角色c:真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
- ConcreteComponent 具体构件角色(真实对象):io流中的FileInputStream、FileOutputStream
- Decorator装饰角色:持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。
- ConcreteDecorator具体装饰角色:负责给构件对象增加新的责任。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-virLAD4w-1580921151459)(decorator.jpg)]
-
IO流实现细节:
- Component抽象构件角色:io流中的InputStream、OutputStream、Reader、Writer
- ConcreteComponent 具体构件角色:io流中的FileInputStream、FileOutputStream
- Decorator装饰角色:持有一个抽象构件的引用:io流中的FilterInputStream、FilterOutputStream
- ConcreteDecorator具体装饰角色:负责给构件对象增加新的责任。Io流中的BufferedOutputStream、BufferedInputStream等。
-
总结:
- 装饰模式(Decorator)也叫包装器模式(Wrapper)
- 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类。
- 优点:扩展对象功能,比继承灵活,不会导致类个数急剧增加 – 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
- 缺点: 产生很多小对象。大量小对象占据内存,一定程度上影响性能。装饰模式易于出错,调试排查比较麻烦。
-
装饰模式和桥接模式的区别:
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。
装饰模式Demo
//Component 抽象构件接口
public interface ICar {
void move();
}
//ConcreteComponent 具体构建对象(被装饰对象)
class Car implements ICar {
@Override
public void move() {
System.out.println("陆地上跑");
}
}
//Decorator 装饰类
class SuperCar implements ICar {
protected ICar car;
public SuperCar(ICar car) {
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//ConcreteDecorator 具体装饰角色 继承装饰类
class FLyCar extends SuperCar {
public FLyCar(ICar car) {
super(car);
}
public void fly() {
System.out.println("天上飞");
}
@Override
public void move() {
super.move();
fly();
}
}
//ConcreteDecorator 具体装饰角色 继承装饰类
class WaterCar extends SuperCar {
public WaterCar(ICar car) {
super(car);
}
public void swim() {
System.out.println("水里游");
}
@Override
public void move() {
super.move();
swim();
}
}
public class Client {
public static void main(String[] args) {
Car car = new Car();
FLyCar fLyCar = new FLyCar(car);
WaterCar waterCar = new WaterCar(fLyCar);
waterCar.move(); //通过多层装饰添加新功能
}
}
十、外观模式(Facade)
-
迪米特法则(最少知识原则):一个软件实体应当尽可能少的与其他实体发生相互作用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tVg5gXh6-1580921151460)(facade2.jpg)] -
外观模式核心:为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vbtu5jo8-1580921151460)(facade.jpg)]
-
开发中常见的场景: 频率很高。哪里都会遇到。各种技术和框架中,都有外观模式的使用。
如: JDBC封装后的,commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC工具类等
十一、享元模式(FlyWeight)
- 核心:
- 享元模式以共享的方式高效地支持大量细粒度对象的重用。
- 享元对象能做到共享的关键是区分了内部状态和外部状态。
内部状态:可以共享,不会随环境变化而改变
外部状态:不可以共享,会随环境变化而改变
- 享元模式实现:
- FlyweightFactory享元工厂类: 创建并管理享元对象,享元池一般设计成键值对
- FlyWeight抽象享元类:通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
- ConcreteFlyWeight具体享元类:为内部状态提供成员变量进行存储
- UnsharedConcreteFlyWeight非共享享元类:不能被共享的子类可以设计为非共享享元类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KSCIZFr-1580921151460)(FlyWeight.jpg)]
- 享元模式开发中应用的场景:
享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池。
String类的设计也是享元模式 - 优点:极大减少内存中对象的数量 – 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能 – 外部状态相对独立,不影响内部状态
- 缺点:模式较复杂,使程序逻辑复杂化。为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间。
享元模式Demo
package FlyWeight;
/**
* 抽象享元类(FlyWeight)
*/
interface ChessFlyWeight {
void setColor(String c);
String getColor();
void display(Coordinate c);
}
/**
* 具体享元类(ConcreteFlyWeigh)
*/
class ConcreateChess implements ChessFlyWeight {
private String color;
public ConcreateChess(String color) {
this.color = color;
}
@Override
public void setColor(String c) {
this.color = c;
}
@Override
public String getColor() {
return color;
}
@Override
public void display(Coordinate c) {
System.out.println("棋子颜色:"+color);
System.out.println("棋子位置"+c.getX()+c.getY());
}
}
package FlyWeight;
/**
* 非共享元类(UnsharedConcreteFlyWeight)
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package FlyWeight;
import java.util.HashMap;
import java.util.Map;
/**
* 享元工厂(FlyweightFactory)
*/
public class ChessFlyWeightFactory {
//享元池
private static Map<String,ChessFlyWeight> map= new HashMap<String,ChessFlyWeight>();
public static ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else{
return new ConcreateChess(color);
}
}
}
package FlyWeight;
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2); //chess1和chess2是同一个对象
//添加外部状态处理
chess1.display(new Coordinate(10,10));
}
}
行为型模式
关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责,共有11种模式。
模式 | 作用 |
---|---|
职责链模式 | 避免请求发送者和接收者耦合,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理为止 |
命令模式 | 将一个请求封装为一个对象,从而使得请求调用者和请求接收者解耦 |
解释器模式 | 描述如何为语言定义一个文法,如何解析 |
迭代器模式 | 提供了一种方法来访问聚合对象 |
中介者模式 | 通过一个中介对象来封装一系列的对象交互,使得各对象不需要相互引用 |
备忘录模式 | 捕获一个对象的内部状态,并保存之;需要时,可以恢复到保存的状态 |
观察者模式 | 当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新 |
状态模式 | 允许一个对象在其内部状态改变时改变它的行为 |
策略模式 | 定义一系列算法,并将每个算法封装在一个类中 |
模板方法 | 定义一个操作的算法骨架,将某些易变的步骤延迟到子类中实现 |
访问者模式 | 表示一个作用于某对象结构中的各元素的操作,它使得用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作 |
十二、职责链模式 (Chain Of Responsibility)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxQ76nuy-1580921151461)(chainOfResponsibility.jpg)]
- 添加新的处理对象: 由于责任链的创建完全在客户端,因此新增新的具体处理者对原有类库没有任何影响,只需添加新的类,然后在客户端调用时添加即可。符合开闭原则。
- 链表方式定义职责链(通过下面案例) 非链表方式实现职责链
通过集合、数组生成职责链更加实用!实际上,很多项目中,每个具体的Handler并不是由开发团队定义的,而是项目上线后由外部单位追加的,所以使用链表方式定义COR链就很困难。
package chainOfResp;
/**
* 封装请假的基本信息
*/
public class LeaveRequest {
private String empName;
private int leaveDays;
private String reason;
public LeaveRequest(String empName, int leaveDays, String reason) {
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
@Override
public String toString() {
return "LeaveRequest{" +
"empName='" + empName + '\'' +
", leaveDays=" + leaveDays +
", reason='" + reason + '\'' +
'}';
}
}
package chainOfResp;
/**
* 抽象类
*/
public abstract class Leader {
protected String name;
protected Leader nextLeader;//责任链上的后继对象
public Leader(String name) {
this.name = name;
}
//设定责任链上的后继对象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
/**
* 处理请求的核心的业务方法
* @param request
*/
public abstract void handelRequest(LeaveRequest request);
}
package chainOfResp;
/**
* 主任 只能审批3天内的请假
*/
public class Director extends Leader{
public Director(String name) {
super(name);
}
@Override
public void handelRequest(LeaveRequest request) {
if (request.getLeaveDays()<3) {
System.out.println(request.toString());
System.out.println("主任审批");
}else{
if (this.nextLeader!=null) {
this.nextLeader.handelRequest(request);
}
}
}
}
package chainOfResp;
/**
* 经理 只能审批10天内的请假
*/
public class Master extends Leader{
public Master(String name) {
super(name);
}
@Override
public void handelRequest(LeaveRequest request) {
if (request.getLeaveDays()<10) {
System.out.println(request.toString());
System.out.println("经理审批");
}else{
if (this.nextLeader!=null) {
this.nextLeader.handelRequest(request);
}
}
}
}
package chainOfResp;
/**
* 总经理 只能审批30天内的请假
*/
public class GeneraMaster extends Leader{
public GeneraMaster(String name) {
super(name);
}
@Override
public void handelRequest(LeaveRequest request) {
if (request.getLeaveDays()<30) {
System.out.println(request.toString());
System.out.println("总经理审批");
}else{
System.out.println("超过30天不能审批");
}
}
}
package chainOfResp;
public class Client {
public static void main(String[] args) {
Leader a = new Director("张三");
Leader b = new Master("李四");
Leader c = new GeneraMaster("王五");
//组织关系链
a.setNextLeader(b);
b.setNextLeader(c);
//开始请假操作
LeaveRequest request1 = new LeaveRequest("TOM",10,"探亲");
a.handelRequest(request1);
}
}
十三、迭代器模式(Iterator、Cursor )
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMEv2AcX-1580921151461)(chainOfResponsibility.jpg)]
- JDK内置的迭代器(List/Set)
- 提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
- 聚合对象:存储数据 – 迭代器:遍历数据
迭代器模式Demo
package iterator;
/**
* 自定义的迭代器接口
*/
public interface MyIterator {
void first(); //将游标指向第一个元素
void next(); //将游标指向下一个元素
boolean hasNext(); //判断是否存在下一个元素
boolean isFirst();
boolean isLast();
Object getCurrentObj(); //获取游标指向当前的对象
}
package iterator;
import java.util.ArrayList;
import java.util.List;
/**
* 创建容器 实现自己的迭代器
*/
public class ConcreteMyAggregate {
private List<Object> list = new ArrayList<Object>();
public void addObject(Object obj) {
this.list.add(obj);
}
public void removeObject(Object obj) {
this.list.remove(obj);
}
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
public MyIterator createIterator() {
return new ConcreateIterator();
}
//使用内部类定义迭代器,可以使用外部类属性
private class ConcreateIterator implements MyIterator {
private int cursor; //定义游标用于记录遍历位置
@Override
public void first() {
cursor = 0;
}
@Override
public void next() {
if (cursor < list.size()) {
cursor++;
}
}
@Override
public boolean hasNext() {
if (cursor < list.size()) {
return true;
}
return false;
}
@Override
public boolean isFirst() {
return cursor == 0;
}
@Override
public boolean isLast() {
return cursor == (list.size() - 1);
}
@Override
public Object getCurrentObj() {
return list.get(cursor);
}
}
}
package iterator;
public class Client {
public static void main(String[] args) {
ConcreteMyAggregate cma = new ConcreteMyAggregate();
cma.addObject("aa");
cma.addObject("BB");
cma.addObject("cc");
MyIterator iterator = cma.createIterator();
while (iterator.hasNext()){
System.out.println(iterator.getCurrentObj());
iterator.next();
}
System.out.println(iterator.isFirst());
}
}
十四、中介者模式(Mediator)
- 核心:如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系极其复杂,这些对象称为“同事对象”。我们可以引入一个中介者对象,使各个同事对象只跟中介这对象打交到,将复杂的网络结构化解为如下星形结构。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ghsNtcYr-1580921151461)(C:\Users\CHEN\Desktop\多线程\madiator1.jpg)] - 中介者模式类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZN4PQQL-1580921151462)(C:\Users\CHEN\Desktop\多线程\madiator2.jpg)] - 中介者模式的本质:解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系
- 开发中常见的场景:
- MVC模式(其中的C,控制器就是一个中介者对象。M和V都和他打交道)
- 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象
- 图形界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者对象来解决,可以是整体的窗口对象或者DOM对象
- Java.lang.reflect.Method#invoke()
中介者模式Demo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RuVSekMK-1580921151462)(C:\Users\CHEN\Desktop\多线程\madiator.jpg)]
package madiator;
/**
* 中介者类接口
*/
public interface Mediator {
void register(String dname,Department d);
void command(String name);
}
package madiator;
// 同事类接口
public interface Department {
void selfAction(); //做本部门的事情
void outAction(); //向总经理发出申请
}
package madiator;
// 同事类实现
public class Development implements Department {
private Mediator m; //持有中介者(总经理)的引用
public Development(Mediator m) {
this.m = m;
m.register("development",this);
}
@Override
public void selfAction() {
System.out.println("科研,开发项目");
}
@Override
public void outAction() {
System.out.println("汇报工作,申请研发费用");
}
}
package madiator;
// 同事类实现
public class Finacial implements Department {
private Mediator m; //持有中介者(总经理)的引用
public Finacial(Mediator m) {
this.m = m;
m.register("finacial",this);
}
@Override
public void selfAction() {
System.out.println("给钱");
}
@Override
public void outAction() {
System.out.println("汇报工作,钱太多了怎么花?");
}
}
/**
* 中介者类实现
*/
public class President implements Mediator {
private Map<String, Department> map = new HashMap<String, Department>();
@Override
public void register(String dname, Department d) {
map.put(dname,d);
}
@Override
public void command(String name) {
map.get(name).selfAction();
}
}
package madiator;
public class Client {
public static void main(String[] args) {
Mediator m = new President();
Department finacial = new Finacial(m);
Department development = new Development(m);
Department market = new Market(m);
market.outAction();
}
}
十五、命令模式(command)
- 结构:
- Command抽象命令类
- ConcreateCommand具体命令类
- Invoker调用者/请求者:请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute(),简介调用接受者的相关操作。
- Receiver接收者:接受执行与请求相关的操作,具体实现对请求的业务处理。未抽象前,实际执行操作内容的对象
- client客户类:在客户类中需创建调用者对象、具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有直接关系,都通过命令对象间接调用。
- 开发中常见的场景:
- Struts2中,action的整个调用过程中就有命令模式。
- 数据库事务机制的底层实现
- 命令的撤销和恢复[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PlaFWYTV-1580921151462)(command.png)]
命令模式Demo
package command;
/**
* 真正命令执行者
*/
public class Receiver {
public void action(){
System.out.println("Receiver.action");
}
}
package command;
public interface Command {
/**
* 实际项目中可以根据需求设计多个不同的方法
*/
void execute();
}
class ConcreateCommand implements Command{
private Receiver receiver; //持有真正命令执行者
public ConcreateCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
//命令执行前后可以做相关处理代码
receiver.action();
}
}
package command;
//调用者/发起者
public class Invoke {
private Command command; //这里可以使用List<Command>容器发起多条命令,进行批处理。数据库低层的事务管理就是类似结构
public Invoke(Command command) {
this.command = command;
}
//业务方法,调用该命令类的方法
public void call(){
//调用命令前后可以可以加入相关代码
command.execute();
}
}
package command;
public class Client {
public static void main(String[] args) {
Command c = new ConcreateCommand(new Receiver());
Invoke i = new Invoke(c);
i.call();
}
}
十六、解释器模式(Interpreter)【了解】
-
是一种不常用的设计模式
-
用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计。
-
当我们需要开发一种新的语言时,可以考虑使用解释器模式。
-
尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用Jruby,Groovy、java的js引擎来替代解释器的作用,弥补java语言的不足。
-
开发中常见的场景: EL表达式式的处理 、 正则表达式解释器 、 SQL语法的解释器 、 数学表达式解析器
-
如现成的工具包:Math Expression String Parser、Expression4J等。
-
MESP的网址: http://sourceforge.net/projects/expression-tree/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XGjpGPfo-1580921151463)(interpreter.jpg)]
十七、访问者模式(Visitor)【了解】
- Expression4J的网址: http://sourceforge.net/projects/expression4j/
- 模式动机:对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其访问方式也有所不同。
- 定义: 表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变个元素的类的前提下定义作用于这些元素的新操作。
- 开发中的场景(应用范围非常窄,了解即可):XML文档解析器设计、编译器的设计、复杂集合对象的处理
十八、策略模式(Strategy)
- 假如,类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难于维护。
- 策略模式:策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法。并且由客户端决定调用哪个算法。
- 本质:分离算法,选择实现。
- 开发中常见的场景:
- JAVASE中GUI编程中,布局管理
- Spring框架中,Resource接口,资源访问策略
- javax.servlet.http.HttpServlet#service()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pjk6YIYl-1580921151463)(Strategy.png)]
普通方式Demo
package strategy;
/**
* 实现起来比较容易 符合一般开发人员思路
* 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护。
* 如果有新增类型,就需要频繁的修改此处代码 不符合开闭原则
*/
public class TestStrategy {
public double getPrice(String type,double price){
if(type.equals("普通客户小批量")){
System.out.println("不打折,原价");
return price;
}else if(type.equals("普通客户大批量")){
System.out.println("打九折");
return price*0.9;
}else if(type.equals("老客户小批量")){
System.out.println("打八五折");
return price*0.85;
}else if(type.equals("老客户大批量")){
System.out.println("打八折");
return price*0.8;
}
return price;
}
}
策略模式Demo
package strategy;
public interface Strategy {
public double getPrice(double standerdPrice);
}
package strategy;
public class NewCustomerFewStrategy implements Strategy{
@Override
public double getPrice(double standerdPrice) {
System.out.println("不打折,原价");
return standerdPrice;
}
}
package strategy;
public class NewCustomerManyStrategy implements Strategy{
@Override
public double getPrice(double standerdPrice) {
System.out.println("打九折");
return standerdPrice*0.9;
}
}
package strategy;
public class OldCustomerFewStrategy implements Strategy{
@Override
public double getPrice(double standerdPrice) {
System.out.println("打八五折");
return standerdPrice*0.85;
}
}
package strategy;
public class OldCustomerManyStrategy implements Strategy{
@Override
public double getPrice(double standerdPrice) {
System.out.println("打八折");
return standerdPrice*0.8;
}
}
package strategy;
/**
* 负责和具体的策略类交互
* 这样的话,具体的算法和直接的客户端调用分离了,使得其算法可以独立与客户端独立的变化
* 如果使用Spring的依赖注入功能,可以通过配置文件动态注入不同的策略对象,动态的切换不同算法
*/
public class Context {
private Strategy strategy;//当前采用的算法对象
//可以通过构造器类注入
public Context(Strategy strategy) {
this.strategy = strategy;
}
//可以通过set方法注入
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void pringPrice(double s){
System.out.println("您的报价是:"+strategy.getPrice(s));
}
}
package strategy;
public class Client {
public static void main(String[] args) {
Strategy s = new OldCustomerFewStrategy();
Context c = new Context(s);
c.pringPrice(998);
}
}
十九、模板方法模式(Template Method)
- 模板方法也被称为方法回调、钩子方法
- 在软件开发中,我们可以将call翻译为调用。子类不能调用父类,而通过父类调用子类。这些调用步骤已经在父类中写好,完全由父类控制整个过程。
- 什么时候用到该模式:实现一个算法时,整体步骤很固定。但是某些部分不固定,不固定的部分可以抽象出来,供子类实现。
- 框架中非常常见如:数据库访问的封装、junit单元测试、servlet中关于doGet/doPost方法调用、Hibernate中模板程序、Spring中jdbcTemplate、HibernateTemplate等
模板方法模式Demo
package templateMethod;
/**
* 模板方法实现:创建抽象类,包含抽象方法和 final整合方法
*/
public abstract class Hook {
public void a() {
System.out.println("Hook.a");
}
public abstract void b(); //抽象方法 等待子类重写
public void c(){
System.out.println("Hook.c");
}
public final void template(){ //将方法整合。不允许子类重写,作为模板方法
a();
b(); //钩子调用字类的方法
c();
}
}
package templateMethod;
public class Client {
public static void main(String[] args) {
Hook h = new TestHook();
h.template();
}
}
//此处可以使用匿名内部类
class TestHook extends Hook{
@Override
public void b() {
System.out.println("TestHook.b");
}
}
二十、状态模式(State)
-
核心:用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
-
结构
- Context环境类:环境类中维护一个State对象,他是定义了当前的状态。
- State抽象状态类:ConcreteState具体状态类 每一个类封装了一个状态对应的行为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U7qdaO2z-1580921151463)(state1.jpg)]
-
开发中常见的场景:
- 银行系统中账号状态的管理
- OA系统中公文状态的管理
- 酒店系统中,房间状态的管理
- 线程对象各状态之间的切换
**- 核心:用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
-
结构
- Context环境类:环境类中维护一个State对象,他是定义了当前的状态。
- State抽象状态类:ConcreteState具体状态类 每一个类封装了一个状态对应的行为
-
开发中常见的场景:
- 银行系统中账号状态的管理
- OA系统中公文状态的管理
- 酒店系统中,房间状态的管理
- 线程对象各状态之间的切换
状态模式Demo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mH5lBMMp-1580921151464)(state.png)]
package state;
/**
* 状态接口
*/
public interface State {
void handle();
}
package state;
public class FreeStatus implements State {
@Override
public void handle() {
System.out.println("房间空闲");
}
}
package state;
public class BookedStatus implements State {
@Override
public void handle() {
System.out.println("房间已预定");
}
}
package state;
public class CheckedInStatus implements State {
@Override
public void handle() {
System.out.println("已入住");
}
}
package state;
public class Context {
private State state;
public void setState(State state) {
System.out.println("Context.setState 修改状态");
this.state = state;
state.handle(); //执行状态所对应的代码
}
}
package state;
public class FreeStatus implements State {
@Override
public void handle() {
System.out.println("房间空闲");
}
}
二十一、观察者模式(Observer)
-
观察者模式主要用于1:N的通知。当一个对象(目标对象Subject或Objservale)的状态变化时,他需要及时告知一系列对象(观察者对象,Observer),令他们做出响应
-
通知观察者方式:
- 推送:每次都会把通知以广播的方式发送给所有观察者,所有观察者只能被动接收。
- 拉取:观察者值要直到有情况即可。至于什么时候获取内容,获取什么内容都可以自主决定。
-
JAVASE提供了
java.util.Observable
和java.util.Observer
来实现观察者模式 -
开发中常见的场景:
- 聊天室程序的,服务器转发给所有客户端
- 网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
- 邮件订阅
- Servlet中,监听器的实现
- Android中,广播机制
- JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event
Model) 事件源-目标对象 ,事件监听器-观察者。 - 商城中,群发某商品打折信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPlF3qc4-1580921151464)(Observer.jpg)]
观察者模式Demo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UelJCVyv-1580921151464)(Observer2.jpg)]
package observer;
/**
* 观察者接口
*/
public interface Observer {
void update(Subject subject); //使用目标对象更新观察者
}
package observer;
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象抽象类,定义用于注册观察者的容器和相关方法以及调用所有观察者更新的方法
*/
public class Subject {
protected List<Observer> list= new ArrayList<Observer>(); //存放所有观察者
public void registerObserver(Observer obs){
list.add(obs);
}
public void removeObserver(Observer obs){
list.remove(obs);
}
//通知所有观察者更新状态
public void notfiyAllObservice(){
for (Observer obs : list) {
obs.update(this);
}
}
}
package observer;
/**
* 目标对象
*/
public class ConcreateSubiect extends Subject{
private int state; //状态码
public int getState() {
return state;
}
public void setState(int state){
this.state = state;
this.notfiyAllObservice(); //当状态发生变化 用父类notfiyAllObservice方法通知所有观察者
}
}
package observer;
public class ObserverA implements Observer {
private int myState; //myState 需要跟目标对象的state值保持一致!
@Override
public void update(Subject subject) {
myState = ((ConcreateSubiect)subject).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
package observer;
public class Client {
public static void main(String[] args) {
//创建目标对象
ConcreateSubiect subiect = new ConcreateSubiect();
//创建观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将观察者对象添加到目标对象的subject观察者容器中
subiect.registerObserver(obs1);
subiect.registerObserver(obs2);
subiect.registerObserver(obs3);
//更改目标对象属性 观察者对象会一起被更改
subiect.setState(1);
}
}
观察者模式JDK实现Demo
package observer2;
import java.util.Observable;
/**
* jdk实现观察者模式 目标类继承Observable类
*/
public class ConcreateSubjext extends Observable {
private int state;
public void set(int s) {
state = s; //目标者对象发生了改变
setChanged(); //表示目标对象已经做了更改
notifyObservers(state); //通知所有观察者
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
package observer2;
import java.util.Observable;
import java.util.Observer;
/**
* jdk实现观察者模式 观察者类实现 java.util.Observer
*/
public class ObserverA implements Observer {
private int myState;
@Override
public void update(Observable o, Object arg) {
myState = ((ConcreateSubjext)o).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
package observer2;
/**
* jdk实现观察者模式
*/
public class Client {
public static void main(String[] args) {
ConcreateSubjext subjext = new ConcreateSubjext();
ObserverA obsA = new ObserverA();
ObserverA obsB = new ObserverA();
ObserverA obsC = new ObserverA();
subjext.addObserver(obsA);
subjext.addObserver(obsB);
subjext.addObserver(obsC);
subjext.set(0);
System.out.println(obsA.getMyState());
System.out.println(obsB.getMyState());
System.out.println(obsC.getMyState());
}
}
二十二、备忘录模式(Memento)
- 核心:保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。
- 结构:源发器类Originator – 备忘录类Memento – 负责人类CareTaker
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTF0kCEn-1580921151465)(memento.jpg)] - 开发中常见的应用场景:
- 棋类游戏中的,悔棋
- 普通软件中的,撤销操作
- 数据库软件中的,事务管理中的,回滚操作
- Photoshop软件中的,历史记录
备忘录模式Demo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fojT1tiQ-1580921151465)(memento2.png)]
package memento;
/**
* 备忘录类
*/
public class EmpMemento {
private String ename;
private int age;
private double salary;
public EmpMemento(Emp e) {
this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSalary();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package memento;
/**
* 源发器类
*/
public class Emp {
private String ename;
private int age;
private double salary;
//进行备份操作 并返回备忘录对象
public EmpMemento memento() {
return new EmpMemento(this);
}
@Override
public String toString() {
return "Emp{" +
"ename='" + ename + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
//进行数据恢复,恢复指定备忘录的值
public void revocery(EmpMemento em) {
this.ename = em.getEname();
this.age = em.getAge();
this.salary = em.getSalary();
}
public Emp(String ename, int age, double salary) {
this.ename = ename;
this.age = age;
this.salary = salary;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package memento;
/**
* 负责人类
* 负责管理备忘录对象
*/
public class CareTaker {
//储存一个 也可以使用list集合或stack(栈)进行多备份点保存,可以序列化持久化操作保存到硬盘
private EmpMemento memento;
public EmpMemento getMemento() {
return memento;
}
public void setMemento(EmpMemento memento) {
this.memento = memento;
}
}
package memento;
public class Client {
public static void main(String[] args) {
CareTaker ct = new CareTaker();
Emp emp = new Emp("张三",18,900);
ct.setMemento(emp.memento());
emp.setEname("李四");
System.out.println(emp.toString());
emp.revocery(ct.getMemento());
System.out.println(emp.toString());
}
}