设计模式的基本要素:模式名称、问题、解决方案、效果
面向对象设计模式可分为以下三类:
序号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
3 | 行为型模式 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern) 业务代表模式(Business Delegate Pattern) 组合实体模式(Composite Entity Pattern) 数据访问对象模式(Data Access Object Pattern) 前端控制器模式(Front Controller Pattern) 拦截过滤器模式(Intercepting Filter Pattern) 服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern) |
开闭原则(Open Close Principle):对扩展开放,对修改关闭
在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
里氏替换原则(Liskov
Substitution
Principle
):继承必须确保超类所拥有的性质在子类中仍然成立
任何基类可以出现的地方,子类一定可以出现。LSP
是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
依赖倒置原则(Dependence Inversion Principle):要面向接口编程,不要面向实现编程
针对接口编程,依赖于抽象而不依赖于具体。
单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性
接口隔离原则(Interface Segregation Principle):要为各个类建立它们需要的专用接口
使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
迪米特法则(Demeter Principle):只与你的直接朋友交谈,不跟”陌生人“说话
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则(Composite Reuse Principle):尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
尽量使用合成/聚合的方式,而不是使用继承。
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
介绍
意图:保证一个类仅有一个实例,并向外提供一个访问它的全局访问点。
何时使用:需要控制实例数目,节省系统资源的时候
如何解决:判断系统释放已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数必须私有。
应用实例
使用场景:
优点
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
注意事项:
getInstance
()方法需要使用同步锁synchronized(Singleton.class
)防止多线程同时进入造成instance被多次实例化实现
创建SingleObject
类。SingleObject
类(单例类)有它的私有构造函数和本身的一个静态实例。SingleObject
类提供了一个静态方法,供外界获取它的静态实例。演示类SingletonPatternDemo
使用SingleObject
类来获取 SingleObject
对象。
步骤一
创建一个Singleton类:SingleObject.java
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject() {}
//获取唯一可用的对象
public static SingleObject getInstance() {
return instance;
}
public void showMessage() {
System.out.println("Hello Singleton.");
}
}
步骤二
从singleton类获取唯一的对象:SingletonPatternDemo.java
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object1 = SingleObject.getInstance();
SingleObject object2 = SingleObject.getInstance();
System.out.println(object1);
System.out.println(object2);
}
}
结果
com.jourwon.designpattern.creational.singleton.SingleObject@1b6d3586
com.jourwon.designpattern.creational.singleton.SingleObject@1b6d3586
单例模式在多线程环境中需要特别注意的问题
假设此时需要实例化的类名称为SingleObject
,在进行实例化的过程中,期望的执行顺序如下:
期望的执行顺序是123,但也有可能是132,如果是在单线程环境下,单例模式是没有问题的,因为至始至终都是一个线程,返回的是就只有一个实例化的对象。
但如果在多线程环境下,需要注意:假如有线程A和线程B,在线程A没有执行完时,线程B进来了,读取到SingleObject
不为空,即判断SingleObject
已经实例化完成,线程B直接返回这个SingleObject
,但线程A还没有执行完成,此时SingleObject
还没有完成构造,此时就会出错,所以需要注意多线程环境下的处理,可以对SingleObject
对象加volatile
关键字,声明此变量为原子变量,也可以在方法加synchronized
锁等…
假如有线程A和线程B,在线程A没有执行完时,线程B进来了,读取到lazyMan不为空,即判断lazyMan已经实例化完成,线程B直接返回这个lazyMan,但线程A还没有执行完成,此时lazyMan还没有完成构造,此时就会出错,所以需要注意多线程环境下的处理,可以对lazyMan对象加volatile关键字,声明此变量为原子变量,也可以在方法加synchronized锁等…
单例模式各种实现方式对比:
实现方式 | 是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|---|
懒汉式 | 是 | 否 | 易 |
懒汉式+synchronizd |
是 | 是 | 易 |
饿汉式 | 否 | 是 | 易 |
双检锁DCL |
是 | 是 | 较复杂 |
静态内部类 | 否 | 是 | 一般 |
枚举 | 否 | 是 | 易 |
volatile关键字 | 是 | 是 | 较复杂 |
ThreadLocal |
是 | 是 | 较复杂 |
CAS 锁 |
是 | 是 | 较复杂 |
单例模式的实现方式如下:
1、懒汉式单例,线程不安全
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 否 | 易 |
描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁synchronized,所以严格意义上它并不算单例模式。
这种lazy loading(延迟加载)很明显,不要求线程安全,在多线程不能正常工作。
代码示例:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
往下的几种实现方式都支持多线程,但是在性能上有所差异。
2、懒汉式单例,线程安全
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 易 |
描述:这种方式具备很好的lazy loading(延迟加载),能够在多线程中很好的工作,但是,效率很低,99%情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费
缺点:必须加锁synchronized才能保证单例,但加锁会影响效率。
getInstance
()的性能对应用程序不是很关键(该方法使用不太频繁)。
代码示例:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
3、饿汉式单例
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
否 | 是 | 易 |
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于classloader
(类加载器)机制避免了多线程的同步问题,不过,singleton在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance
方法,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化singleton显然没有达到lazy loading(延迟加载)的效果。
代码示例:
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
4、双检锁/双重校验锁(DCL
,即double-checked locking)
JDK
版本:JDK
1.5起
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 较复杂 |
描述:这种方式采用双锁机制,安全且在多线程情况下保持高性能。
getInstance
()的性能对应用程序很关键。
优点:线程安全;延迟加载;效率较高。
缺点:JVM
编译器的指令重排导致单例出现漏洞。
代码示例:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
在该方法中对instance进行了两次判空:第一层判断为了避免不必要的同步,第二层判断则是为了在null的情况下创建实例。对第六种单例的漏洞进行了弥补,但是还是有点小问题的,问题就在instance = new Singleton();语句上。
这语句在这里看起来是一句代码,但实际上它并不是一个原子操作,这句代码最终会被编译成多条汇编指令,它大致做了3件事情:
但是,由于Java编译器运行处理器乱序执行,以及
jdk1.5
之前Java内存模型中Cache、寄存器到主内存会写顺序的规定,上面的第二和第三的顺序是无法保证的。也就是说,执行顺序可能是1-2-3也可能是1-3-2.如果是后者,并且在3执行完毕、2未执行之前,被切换到线程B上,这时候instance因为已经在线程A内执行3了,instance已经是非null,所有线程B直接取走instance,再使用时就会出错,这就是DCL
失效问题,而且这种难以跟踪难以重现的问题很可能会隐藏很久。
5、登记式/静态内部类
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 一般 |
描述:这种方式能达到双检锁方式一样的效果,但实现简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了classloader
机制来保证初始化instance时只有一个线程,它跟3种方式不同的是:第3种方式只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder
类没有被主动使用,只有显示通过调用getInstance
方法时,才会显示装载SingletonHolder
类,从而实例化instance。想象一下,如果实例化instance很消耗资源,所以想让它延迟加载,另外一方面,又不希望在Singleton类加载时就实例化,因为不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第3种方式就显得很合理。
代码示例:
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6、枚举
JDK
版本:JDK
1.5起
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
否 | 是 | 易 |
描述:这种实现方式还没有被广泛使用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于JDK
1.5之后才加入enum
特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少使用。不能通过reflection attack来调用私有构造方法。使用时通过Singleton.INSTANCE
来访问实例即可。
代码示例:
public enum Singleton{
INSTANCE;
}
7、volatile关键字
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 较复杂 |
描述:对于Double-Check这种可能出现的问题(当然这种概率已经非常小了,但还是有的),解决方案是:只需要给instance的声明加上volatile关键字即可volatile关键字的一个作用是禁止指令重排,把instance声明为volatile之后,对它的写操作就会有一个内存屏障,这样,在它的赋值完成之前,就不会调用读操作。注意:volatile阻止的不是singleton=new Singleton()这句话内部[1-2-3]的指令重排,而是保证了在一个写操作([1-2-3])完成之前,不会调用读操作(if(instance==null))。
代码示例:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
8、使用ThreadLocal
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 较复杂 |
描述:ThreadLocal
会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal
采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
代码示例:
public class Singleton {
private static final ThreadLocal<Singleton> threadLocalSingleton = new ThreadLocal<>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
private Singleton() {
}
public static Singleton getInstance() {
return threadLocalSingleton.get();
}
}
9、使用CAS
锁
是否Lazy初始化 | 是否多线程安全 | 实现难度 |
---|---|---|
是 | 是 | 较复杂 |
代码示例:
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private Singleton() {
}
public static Singleton getInstance() {
for (; ; ) {
Singleton current = INSTANCE.get();
if (current != null) {
return current;
}
current = new Singleton();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
}
**补充:**一般情况下,不建议使用第1种和第2种懒汉方式,建议使用第3种饿汉方式。只有在明确实现lazy loading效果时,才会使用第5种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第6种枚举方式。如果没有其他特殊的需求,可以考虑使用第4种双检锁方式。
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
介绍
意图:将一个复杂的对象的构建与它的表示进行分离,使得同样的构建过程可以创建不同的表示。
主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
何时使用:一些基本部件不会变,而其组合经常变化的时候。
**如何解决:**将变与不变分离开。
**关键代码:**建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例:
Java
中的StringBuilder
优点:
缺点:
使用场景:
需要生成的产品对象有复杂的内部结构,这些产品对象具备共性。
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
需要生成的对象内部属性本身相互依赖。
建造者与抽象工厂模式的比较:
建造者模式更加关注与零件装配的顺序
与抽象工厂模式相比,建造模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件得组装可以返回一辆完整得汽车!
实现
下图示例是Builder模式的常规用法,导演类Director在Builder模式中具有很重要的作用,它用于指定具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品
比如:比如麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。
假设造房简化为:以下步骤:(1)地基(2)钢筋工程(3)铺电线(4)粉刷;“如果”要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品),最后验收。
步骤一:
产品:Product.java
//产品:房子
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
步骤二:
抽象的建造者:Builder.java
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//钢筋工程
abstract void buildC();//铺电线
abstract void buildD();//粉刷
//完工:得到产品
abstract Product getProduct();
}
步骤三:
建造者实现:Worker.java
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildB("钢筋");
System.out.println("钢筋");
}
@Override
void buildC() {
product.setBuildC("电线");
System.out.println("电线");
}
@Override
void buildD() {
product.setBuildD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
步骤四:
指挥者:Director.java
//指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
public class Director {
//指挥工人按照顺序建房子
public Product build(Builder builder) {
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
步骤五:
演示建造者模式:Test.java
public class Test {
public static void main(String[] args) {
//指挥
Director director = new Director();
//指挥具体的工人完成产品
Product product = director.build(new Worker());
System.out.println(product.toString());
}
}
结果
地基
钢筋
电线
粉刷
Product{buildA='地基', buildB='钢筋', buildC='电线', buildD='粉刷'}
Process finished with exit code 0
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
介绍
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行时期建立和删除原型。
何时使用:
当一个系统应该独立于它的产品创建,构成和表示时
当要实例化的类是在运行时刻指定时,例如,通过动态装载。
为了避免创建一个与产品类层次平行的工厂类层次时
当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
关键代码:
实现克隆操作,在Java继承Cloneable
,重写clone()来实现对象的浅拷贝或通过序列化的方式来实现深拷贝
原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。
应用实例:
优点:
缺点:
Cloneable
接口使用场景:
注意事项:
与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现Cloneable
,重写,深拷贝是通过实现Serializable
读取二进制流。
克隆是以某个对象为原型,创建新的对象;new是创建了新的对象。克隆出来的新对象,修改时不会影响原来的对象。克隆出来的对象和原来的对象是一样的。
浅克隆:
public class Video implements Cloneable{
private String name;
private Date createTime;
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public Video() {
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Video object_1 = new Video("AAA", new Date());
System.out.println(object_1.toString());
System.out.println("Object HashCode : "+object_1.hashCode());
Video object_2 = (Video) object_1.clone();
System.out.println(object_2.toString());
System.out.println("Object HashCode : "+object_2.hashCode());
System.out.println("===================");
}
}
Video{name='AAA', createTime=Wed Aug 19 22:47:57 CST 2020}
Object HashCode : 1836019240
Video{name='AAA', createTime=Wed Aug 19 22:47:57 CST 2020}
Object HashCode : 325040804
===================
进程完成,退出码 0
深克隆:
public class Video implements Cloneable{
private String name;
private Date createTime;
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public Video() {
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
//实现深克隆
Video video = (Video) object;
//将这个对象的属性也进行克隆
video.createTime = (Date) this.createTime.clone();
return object;
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
Video object_1 = new Video("AAA", date);
System.out.println(object_1.toString());
System.out.println("Object HashCode : "+object_1.hashCode());
Video object_2 = (Video) object_1.clone();
System.out.println(object_2.toString());
System.out.println("Object HashCode : "+object_2.hashCode());
System.out.println("===================");
date.setTime(22131231);
System.out.println(object_1.toString());
System.out.println(object_2.toString());
}
}
Video{name='AAA', createTime=Wed Aug 19 22:48:40 CST 2020}
Object HashCode : 1836019240
Video{name='AAA', createTime=Wed Aug 19 22:48:40 CST 2020}
Object HashCode : 325040804
===================
Video{name='AAA', createTime=Thu Jan 01 14:08:51 CST 1970}
Video{name='AAA', createTime=Wed Aug 19 22:48:40 CST 2020}
进程完成,退出码 0
实现
创建一个抽象类Shape
和扩展了Shape
类的实体类。下一步是定义类ShapeCache
,该类把对象shape
存储在一个HashTable
中,并在请求的时候返回它们的克隆。演示类PrototypePatternDemo
使用 ShapeCache
类来获取 Shape 对象。
步骤 1
创建一个实现了 Clonable
接口的抽象类:Shape.java
public abstract class Shape implements Cloneable{
private String id;
protected String type;
abstract void draw();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
步骤 2
创建扩展了上面抽象类的实体类:Rectangle.java
public class Rectangle extends Shape{
public Rectangle() {
type = "Rectangle";
}
@Override
void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square extends Shape{
public Square() {
type = "Square";
}
@Override
void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle extends Shape{
public Circle() {
type = "Circle";
}
@Override
void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤 3
创建一个类,从数据库获取实体类,并把它们存储在一个 HashTable
中:ShapeCache.java
public class ShapeCache {
private static Hashtable<String, Shape> shapeTable = new Hashtable<>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeTable.get(shapeId);
return cachedShape;
}
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeTable.put(circle.getId(), circle);
Square square = new Square();
square.setId("2");
shapeTable.put(square.getId(), square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeTable.put(rectangle.getId(), rectangle);
}
}
步骤 4
PrototypePatternDemo
使用ShapeCache
类来获取存储在Hashtable
中的形状的克隆:PrototypePatternDemo.java
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
结果
Shape : Circle
Shape : Square
Shape : Rectangle
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
介绍
**意图:**定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
**主要解决:**主要解决接口选择的问题。
**何时使用:**我们明确地计划不同条件下创建不同实例时。
**如何解决:**让其子类实现工厂接口,返回的也是一个抽象的产品。
**关键代码:**创建过程在其子类执行。
核心本质:
应用实例:
JDK
中Calendar
的getInstance
方法JDBC
中的Connection
对象的获取Spring
中IOC
容器创建管理bean
对象Class
对象的new Instance
方法优点:
缺点:
使用场景:
POP3
"、"IMAP
"、"HTTP
",可以把这三个作为产品类,共同实现一个接口。注意事项:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
简单工厂模式(静态工厂模式)
Tips:所有产品的生产都在一个工厂中进行,添加新的产品时,需要在工厂中添加新的方法生产此产品。
Creator:是简单工厂方法模式的核心,包含应用程序所需要的业务逻辑,当客户类Client需要的时候,委托工厂类创建产品类的对象(工厂类)
Product:可以是Java接口或者Java抽象类,是具体子类的超类或者共同接口(产品接口)
ConcreteProduct
:实现Product接口,或者继承抽象类Product。(具体产品类)
示例类图:
步骤一
创建汽车产品接口:Car.java
public interface Car {
void name();
}
步骤二
创建汽车产品接口实现类:DaZong,Tesla,WuLing
public class DaZong implements Car{
@Override
public void name() {
System.out.println("大众");
}
}
public class Tesla implements Car{
@Override
public void name() {
System.out.println("特斯拉");
}
}
public class WuLing implements Car{
@Override
public void name() {
System.out.println("五菱");
}
}
步骤三
创建汽车工厂:CarFactory.java
public class CarFactory {
/**
* 方式一
* @param car 汽车品牌
* @return 对应汽车实例
*/
public static Car getCar(String car) {
if (car.equals("五菱")) {
return new WuLing();
} else if (car.equals("特斯拉")) {
return new Tesla();
} else if (car.equals("大众")) {
return new DaZong();
} else {
return null;
}
}
/**
* 方式二
* @return 对应汽车实例
*/
public static Car getWuLing() {
return new WuLing();
}
public static Car getTesla() {
return new Tesla();
}
public static Car DaZong() {
return new DaZong();
}
}
步骤四
创建消费者:Consumer.java
public class Consumer {
public static void main(String[] args) {
//使用工厂创建
Car car1 = CarFactory.getCar("五菱");
Car car2 = CarFactory.getCar("特斯拉");
Car car3 = CarFactory.getCar("大众");
car1.name();
car2.name();
car3.name();
}
}
结果
结果:
五菱
特斯拉
大众
进程完成,退出码 0
工厂方法模式
Tips:一个产品对应一个工厂,一个工厂只生产一个产品,添加一个新的产品时,添加一个新的产品与生产该产品的工厂即可,无需在原有代码上进行修改。
public interface Car {
void name();
}
public class MoBai implements Car{
@Override
public void name() {
System.out.println("摩拜单车");
}
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱");
}
}
public interface CarFactory {
Car getCar();
}
public class MoBaiFactory implements CarFactory{
@Override
public MoBai getCar() {
return new MoBai();
}
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
public class Consumer {
public static void main(String[] args) {
Car car1 = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
Car car3 = new MoBaiFactory().getCar();
car1.name();
car2.name();
car3.name();
}
}
结果:
五菱
特斯拉
摩拜单车
进程完成,退出码 0
**抽象工厂模式(Abstract Factory Pattern)**是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
Tips:超级工厂生产多个其他工厂,一个工厂可以生产多种产品;抽象工厂模式不可以增加产品,可以增加产品族
介绍
意图:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类,每个生成的工厂都能按照工厂模式提供对象。
**主要解决:**主要解决接口选择的问题。
**何时使用:**系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
**如何解决:**在一个产品族里面,定义多个产品。
**关键代码:**在一个工厂里聚合多个同类产品。
**应用实例:**工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
优点:
缺点:
规定了所有可能被创建的产品的集合,产品族中扩展新的产品困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
增加了系统的抽象性和理解难度
使用场景:
QQ
换皮肤,一整套一起换**注意事项:**产品族难扩展,产品等级易扩展。
实现
步骤一
产品接口:IphoneProduct,IRouterProduct
//手机
public interface IphoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
//路由器
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
步骤二
产品实现类:HuaweiPhone,HuaweiRouter,XiaomiPhone,XiaomiRouter
//华为手机
public class HuaweiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void callup() {
System.out.println("华为手机打电话");
}
@Override
public void sendSMS() {
System.out.println("华为手机发送短信");
}
}
//华为路由
public class HuaweiRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("华为路由器开机");
}
@Override
public void shutdown() {
System.out.println("华为路由器关机");
}
@Override
public void openWifi() {
System.out.println("华为路由器开启wifi");
}
@Override
public void setting() {
System.out.println("华为路由器设置密码");
}
}
//小米手机
public class XiaomiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("小米手机发送短信");
}
}
//小米路由
public class XiaomiRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("小米路由器开机");
}
@Override
public void shutdown() {
System.out.println("小米路由器关机");
}
@Override
public void openWifi() {
System.out.println("小米路由器开启wifi");
}
@Override
public void setting() {
System.out.println("小米路由器设置密码");
}
}
步骤三
工厂接口:IProductFactory.java
public interface IProductFactory {
/**
* 生产手机
*/
IphoneProduct iphoneProduct();
/**
* 生产路由器
*/
IRouterProduct routerProduct();
}
步骤四
工厂接口实现类:HuaweiFactory,XiaomiFactory
//华为工厂
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new HuaweiRouter();
}
}
//小米工厂
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new XiaomiRouter();
}
}
步骤五
测试抽象工厂模式:Client.java
public class Client {
public static void main(String[] args) {
System.out.println("==========小米系列产品==========");
//小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.callup();
iphoneProduct.sendSMS();
IRouterProduct routerProduct = xiaomiFactory.routerProduct();
routerProduct.openWifi();
System.out.println("===========华为产品==========");
HuaweiFactory huaweiFactory = new HuaweiFactory();
iphoneProduct = huaweiFactory.iphoneProduct();
routerProduct = huaweiFactory.routerProduct();
iphoneProduct.sendSMS();
iphoneProduct.callup();
routerProduct.openWifi();
}
}
结果
==========小米系列产品==========
小米手机打电话
小米手机发送短信
小米路由器开启wifi
===========华为产品==========
华为手机发送短信
华为手机打电话
华为路由器开启wifi
进程完成,退出码 0
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
**意图:**为其他对象提供一种代理以控制对这个对象的访问。
**主要解决:**在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
**何时使用:**想在访问一个类时做一些控制。
**如何解决:**增加中间层。
**关键代码:**实现与被代理类组合。
应用实例:
spring aop
。优点:
缺点:
使用场景:
按职责来划分,通常有以下使用场景:
注意事项:
实现
静态代理
静态靠一个接口类(代理对象)去实现另一个接口(真实对象)。动态靠一个类继承innovation类,然后用set或构造器方法注入真实对象,然后生成代理对象。
优点:
缺点:
角色分析:
示例代码:
//租房
public interface Rent{
public void rent();
}
//出租房子
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东————>出租房子");
}
}
//代理房子
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
System.out.println("代理(中介)————>帮忙找房子租给客户");
}
public void seeHouse() {
System.out.println("代理(中介)————>带你看房");
}
public void hetong() {
System.out.println("代理(中介)————>签租凭合同");
}
public void fare() {
System.out.println("代理(中介)————>收中介费");
}
}
//客户端
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理(中介),帮房东出租房子,但是呢?代理一般会有一些附属操作,比如说收中介费
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房即可
proxy.rent();
}
}
房东————>出租房子
代理(中介)————>帮忙找房子租给客户
进程完成,退出码 0
动态代理
JDK
动态代理cglib
java
字节码实现:javasist
动态代理需要了解两个核心类:Proxy
(代理类),InvocationHandler
(调用处理程序)
动态代理代理类没有真实存在,代理类是动态生成的
优点:
示例代码:
public interface Rent{
public void rent();
}
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东————>出租房子");
}
}
public class ProxyInvocationHandler implements InvocationHandler {
/**
* 被代理的接口
*/
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* 生成得到代理类
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
/**
* 处理代理实例,并返回结果
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质就是使用反射机制实现
Object result = method.invoke(rent, args);
fare();
return result;
}
public void seeHouse() {
System.out.println("代理(中介————>中介带客户看房子");
}
public void fare() {
System.out.println("代理(中介)————>中介收取代理费");
}
}
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在还没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
//获得代理对象
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
结果
代理(中介————>中介带客户看房子
房东————>出租房子
代理(中介)————>中介收取代理费
进程完成,退出码 0
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。它结合了两个独立接口的功能。这种模式设计到一个单一的类,该类负责加入独立的或不兼容的接口功能。
介绍
意图:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些“现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
系统需要使用现有的类,而此类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
想要建立一个可以重复使用的类,用于与一些彼此之间没有台太大关联关系的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口
通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口)。
如何解决:继承或依赖(推荐)
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
应用实例:
美国电器110V
,中国220V
,就要有一个适配器将110V
转换为220V
。
Java
JDK
1.1提供了Enumeration
接口,而在1.2中提供了Iterator
接口,想要使用1.2的JDk
,则要将以前系统的Enumeration
接口转换为Iterator
接口,这时就需要适配器模式
在Linux
上运行Windows
程序
Java
中的JDBC
读卡器是作为内存卡和笔记本之间的适配器。将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
音频播放器设备只能播放MP3
文件,通过使用一个更高级的音频播放器来播放vlc
和MP4
文件。
USB
网线转换器。
优点:
缺点
使用场景:
注意事项:
实现:
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口(USB
接口)。需要适配的类:需要适配的类或者适配者类(网线接口)。适配器:通过包装一个需要适配的对象,把原接口转换成目标对象(USB
转换器,包装网线)。
步骤一
USB
接口:NetToUsb.java
public interface NetToUsb {
//作用:处理请求,把网线插到usb上
public void handleRequest();
}
步骤二
转换器(类适配器):Adapter.java
public class Adapter extends Cable implements NetToUsb{
@Override
public void handleRequest() {
//可以上网了
super.request();
}
}
或者对象适配器:Adapter2.java
public class Adapter2 extends Cable implements NetToUsb{
private Cable cable;
public Adapter2(Cable cable) {
this.cable = cable;
}
@Override
public void handleRequest() {
//可以上网了
cable.request();
}
}
步骤三:
网线:Cable.java
public class Cable {
public void request() {
System.out.println("连接网线上网");
}
}
步骤四
电脑:Computer.java
public class Computer {
//我们的电脑需要连接上转接器才能上网
public void net(NetToUsb netToUsb) {
//上网的具体实现,找一个转接头
netToUsb.handleRequest();
}
public static void main(String[] args) {
//电脑
Computer computer = new Computer();
//网线
Cable cable = new Cable();
//将网线插入转接器
Adapter2 adapter = new Adapter2(cable);
//将转接器插入电脑,开始上网
computer.net(adapter);
//或者
//将网线插入转接器
//Adapter adapter = new Adapter();
//将转接器插入电脑,开始上网
//computer.net(adapter);
}
}
结果
连接网线上网
Process finished with exit code 0
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。这种类型的设计模式属于结构型模式,又称为柄体模式(Handle and Body)或接口模式(Interface),它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式设计到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
介绍
定义:将抽象部分与实现部分分离,使它们都可以独立的变化
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间的耦合。
关键代码:抽象类依赖实现类。
应用实例:
Java
语言通过Java
虚拟机实现了平台的无关系AWT
中的Peer
架构JDBC
驱动程序也是桥接模式的应用之一优点:
缺点:
使用场景:
注意事项:
实现
创建作为桥接实现的Brand
接口和实现了Brand
接口的实体类 Lenovo
、Apple
。Computer
是一个抽象类,将使用 Brand
的对象。演示类Test
使用 Computer
类来组合不同品牌的电脑。
步骤一
创建桥接实现接口:Brand.java
//品牌
public interface Brand {
void info();
}
步骤二
创建实现了Brand
接口的实体桥接实现类:Lenovo.java
//联想品牌
public class Lenovo implements Brand{
@Override
public void info() {
System.out.print("联想品牌");
}
}
Apple.java
public class Apple implements Brand {
@Override
public void info() {
System.out.print("苹果品牌");
}
}
步骤三
使用Brand
接口创建抽象类Computer
:Computer.java
//抽象的电脑类型类
public abstract class Computer {
//组合,品牌 桥
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info() {
//自带品牌
brand.info();
}
}
步骤四
创建实现了Computer接口的实体类:Desktop.java
public class Desktop extends Computer {
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
Laptop.java
public class Laptop extends Computer {
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本");
}
}
步骤五
使用Computer和Brand组合不同品牌的电脑:Test.java
public class Test {
public static void main(String[] args) {
//苹果笔记本
Computer computer = new Laptop(new Apple());
computer.info();
//联想台式机
Computer computer1 = new Desktop(new Lenovo());
computer1.info();
}
}
结果
苹果品牌笔记本
联想品牌台式机
进程完成,退出码 0
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
实现
创建一个Person
对象、Criteria
接口和实现了该接口的实体类,来过滤Person
对象的列表。CriteriaPatternDemo
,演示类使用Criteria
对象,基于各种标准和它们的结合来过滤Person
对象的列表。
步骤一
创建一个类,在该类上应用标准:Person.java
public class Person {
private String name;
private String gender;
private String maritalStatus;
public Person(String name, String gender, String maritalStatus) {
this.name = name;
this.gender = gender;
this.maritalStatus = maritalStatus;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getMaritalStatus() {
return maritalStatus;
}
}
步骤二
为标准(Criteria)创建一个接口:Criteria.java
import java.util.List;
public interface Criteria {
public List<Person> meetCriteria(List<Person> persons);
}
步骤三
创建实现了Criteria接口的实体类:CriteriaMale.java
import java.util.ArrayList;
import java.util.List;
public class CriteriaMale implements Criteria {
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> malePersons = new ArrayList<Person>();
for (Person person : persons) {
if(person.getGender().equalsIgnoreCase("MALE")){
malePersons.add(person);
}
}
return malePersons;
}
}
CriteriaFemale.java
import java.util.ArrayList;
import java.util.List;
public class CriteriaFemale implements Criteria {
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> femalePersons = new ArrayList<Person>();
for (Person person : persons) {
if(person.getGender().equalsIgnoreCase("FEMALE")){
femalePersons.add(person);
}
}
return femalePersons;
}
}
CriteriaSingle.java
import java.util.ArrayList;
import java.util.List;
public class CriteriaSingle implements Criteria {
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> singlePersons = new ArrayList<Person>();
for (Person person : persons) {
if(person.getMaritalStatus().equalsIgnoreCase("SINGLE")){
singlePersons.add(person);
}
}
return singlePersons;
}
}
AndCriteria.java
import java.util.List;
public class AndCriteria implements Criteria {
private Criteria criteria;
private Criteria otherCriteria;
public AndCriteria(Criteria criteria, Criteria otherCriteria) {
this.criteria = criteria;
this.otherCriteria = otherCriteria;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> firstCriteriaPersons = criteria.meetCriteria(persons);
return otherCriteria.meetCriteria(firstCriteriaPersons);
}
}
OrCriteria.java
import java.util.List;
public class OrCriteria implements Criteria {
private Criteria criteria;
private Criteria otherCriteria;
public OrCriteria(Criteria criteria, Criteria otherCriteria) {
this.criteria = criteria;
this.otherCriteria = otherCriteria;
}
@Override
public List<Person> meetCriteria(List<Person> persons) {
List<Person> firstCriteriaItems = criteria.meetCriteria(persons);
List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons);
for (Person person : otherCriteriaItems) {
if(!firstCriteriaItems.contains(person)){
firstCriteriaItems.add(person);
}
}
return firstCriteriaItems;
}
}
步骤四
使用不同的标准(Criteria)和它们的结合来过滤Person对象的列表:CriteriaPatternDemo.java
import java.util.ArrayList;
import java.util.List;
public class CriteriaPatternDemo {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
persons.add(new Person("Robert","Male", "Single"));
persons.add(new Person("John","Male", "Married"));
persons.add(new Person("Laura","Female", "Married"));
persons.add(new Person("Diana","Female", "Single"));
persons.add(new Person("Mike","Male", "Single"));
persons.add(new Person("Bobby","Male", "Single"));
Criteria male = new CriteriaMale();
Criteria female = new CriteriaFemale();
Criteria single = new CriteriaSingle();
Criteria singleMale = new AndCriteria(single, male);
Criteria singleOrFemale = new OrCriteria(single, female);
System.out.println("Males: ");
printPersons(male.meetCriteria(persons));
System.out.println("\nFemales: ");
printPersons(female.meetCriteria(persons));
System.out.println("\nSingle Males: ");
printPersons(singleMale.meetCriteria(persons));
System.out.println("\nSingle Or Females: ");
printPersons(singleOrFemale.meetCriteria(persons));
}
public static void printPersons(List<Person> persons){
for (Person person : persons) {
System.out.println("Person : [ Name : " + person.getName()
+", Gender : " + person.getGender()
+", Marital Status : " + person.getMaritalStatus()
+" ]");
}
}
}
结果
Males:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : John, Gender : Male, Marital Status : Married ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Females:
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Single Males:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Single Or Females:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
进程完成,退出码 0
**外观模式(Facade Pattern)**隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
介绍
**意图:**为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
何时使用:
**如何解决:**客户端不与系统耦合,外观类与系统耦合。
**关键代码:**在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。
应用实例:
优点:
缺点:
使用场景:
注意事项:
实现
创建一个Shape
接口和实现了Shape
接口的实体类。下一步时定义一个外观类ShapeMaker
。ShapeMaker
类使用实体类来代表用户对这些类的调用。演示类FacadePatternDemo
使用ShapeMaker
类来显示结果。
步骤一
创建一个接口:Shape.java
public interface Shape {
void draw();
}
步骤二
创建实现接口的实体类:Rectangle.java
public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
Circle.java
public class Circle implements Shape{
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
步骤三
创建一个外观类:ShapeMaker.java
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle() {
circle.draw();
}
public void drawRectangle() {
rectangle.draw();
}
public void drawSquare() {
square.draw();
}
}
步骤四
使用该外观类画出各种类型的形状:FacadePatternDemo.java
public class FacadePatternDemo {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
结果
Circle::draw()
Rectangle::draw()
Square::draw()
进程完成,退出码 0
**享元模式(Flyweight Pattern)**主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。通过创建5个对象来画出20个分布于不同位置的圆来演示这种模式。由于只有5种可用的颜色,所以color属性被用来检查现有的Circle对象。
介绍
**意图:**运用共享技术有效地支持大量细粒度的对象。
**主要解决:**在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用:
**如何解决:**用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
**关键代码:**用HashMap
存储这些对象。
应用实例:
优点:
缺点:
使用场景:
注意事项:
注意划分外部状态和内部状态,否则可能会引起线程安全问题。
这些类必须有一个工厂对象加以控制。
实现
创建一个Shape
接口和实现了Shape
接口的实体类Circle
。下一步使定义工厂类ShapeFactory
。ShapeFactory
有一个Circle
的HashMap
,其中键名为Circle
对象的颜色。无论何时接受到请求,都会创建一个特定颜色的圆。ShapeFactory
检查它的HashMap
中的circle
对象,如果找到Circle
对象,则返回该对象,否则将创建一个存储在hashmap
中以备后续使用的新对象,并把该对象返回到客户端。
演示类FlyWeightPatternDemo
使用ShapeFactory
来获取Shape
对象。它将向ShapeFactory
传递信息(red/green/blue/black/white
),以便获取它所需对象的颜色。
步骤一
创建一个接口:Shape.java
public interface Shape {
void draw();
}
步骤二
创建实现接口的实体类:Circle.java
public class Circle implements Shape{
private String color;
private int x;
private int y;
private int radius;
public Circle(String color) {
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : "+color
+", x : "+x+", y :"+y+", radius :"+radius);
}
}
步骤三
创建一个工厂,生成基于给定信息的实体类的对象:ShapeFactory.java
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : "+color);
}
return circle;
}
}
步骤四
使用该工厂,通过传递颜色信息来获取实体类的对象:FlyweightPatternDemo.java
public class FlyweightPatternDemo {
private static final String colors[] =
{"Red", "Green", "Blue", "White", "Black"};
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
Circle circle =
(Circle) ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
private static int getRandomX() {
return (int)(Math.random() * 100);
}
private static int getRandomY() {
return (int) (Math.random() * 100);
}
}
结果
Creating circle of color : Black
Circle: Draw() [Color : Black, x : 13, y :78, radius :100
Circle: Draw() [Color : Black, x : 61, y :52, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 51, y :59, radius :100
Circle: Draw() [Color : White, x : 51, y :34, radius :100
Circle: Draw() [Color : White, x : 67, y :54, radius :100
Circle: Draw() [Color : White, x : 32, y :27, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 42, y :94, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 60, y :11, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 5, y :46, radius :100
Circle: Draw() [Color : Green, x : 60, y :72, radius :100
Circle: Draw() [Color : Green, x : 59, y :57, radius :100
Circle: Draw() [Color : Green, x : 83, y :89, radius :100
Circle: Draw() [Color : White, x : 40, y :7, radius :100
Circle: Draw() [Color : Green, x : 85, y :27, radius :100
Circle: Draw() [Color : Green, x : 93, y :14, radius :100
Circle: Draw() [Color : White, x : 85, y :87, radius :100
Circle: Draw() [Color : Green, x : 92, y :51, radius :100
Circle: Draw() [Color : Black, x : 3, y :43, radius :100
Circle: Draw() [Color : Green, x : 56, y :66, radius :100
Circle: Draw() [Color : Green, x : 76, y :21, radius :100
进程完成,退出码 0
**装饰器模式(Decorator Pattern)**允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包类。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。通过下面的实例来演示装饰器模式的用法。其中,将把一个形状装饰上不同的颜色,同时又不改变形状类。
介绍
**意图:**动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
**主要解决:**一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
**何时使用:**在不想增加很多子类的情况下扩展类。
**如何解决:**将具体功能职责划分,同时继承装饰者模式。
关键代码:
应用实例:
优点:
缺点:
使用场景:
注意事项:
实现
创建一个Shape
接口和实现了Shape
接口的实体类。然后创建一个实现了Shape
接口的抽象装饰类ShapeDecorator
,并把Shape对象作为它的实例变量。RedShapeDecorator
是实现了ShapeDecorator
的实体类。演示类DecoratorPatternDemo
使用RedShapeDecorator
来装饰Shape对象。
步骤一
创建一个接口:Shape.java
public interface Shape {
void draw();
}
步骤二
创建实现接口的实体类:Rectangle.java
public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
Circle.java
public class Circle implements Shape{
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
步骤三
创建实现了Shape
接口的抽象装饰类:ShapeDecorator.java
public class ShapeDecorator implements Shape{
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
@Override
public void draw() {
decoratedShape.draw();
}
}
步骤四
创建扩展了ShapeDecorator
类的实体装饰类:RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator{
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
}
步骤五
使用RedShapeDecorator
来装饰Shape对象:DecoratorPatternDemo.java
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
结果:
Circle with normal border
Shape: Circle
Circle of red border
Shape: Circle
Border Color: Red
Rectangle of red border
Shape: Rectangle
Border Color: Red
进程完成,退出码 0
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。通过下面的实例来演示组合模式的用法。实例演示了一个组织中员工的层次结构。
介绍
**意图:**将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
**主要解决:**它在我们树形结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用:
如何解决:
关键代码:
应用实例:
JAVA AWT
和SWING
中,对于Button
和Checkbox
是树叶,Container
是树枝。优点:
缺点:
使用场景:
注意事项:
实现
Employee
类被当作组合模型类。CompositePatternDemo
,演示类使用Employee
类来添加部门层次结构,并打印所有员工。
步骤一
创建Employee
类,该类带有Employee
对象的列表:Employee.java
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
public Employee(String name, String dept, int salary) {
this.name = name;
this.dept = dept;
this.salary = salary;
subordinates = new ArrayList<Employee>();
}
public void addEmployee(Employee employee) {
subordinates.add(employee);
}
public void removeEmployee(Employee employee) {
subordinates.remove(employee);
}
public List<Employee> getSubordinates() {
return subordinates;
}
@Override
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
步骤二
使用Employee类来创建和打印员工的层次结构:CompositePatternDemo.java
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO",30000);
Employee headSales = new Employee("Robert", "Head Sales", 20000);
Employee headMarketing = new Employee("Michel", "Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.addEmployee(headSales);
CEO.addEmployee(headMarketing);
headSales.addEmployee(salesExecutive1);
headSales.addEmployee(salesExecutive2);
headMarketing.addEmployee(clerk1);
headMarketing.addEmployee(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
结果
Employee :[ Name : John, dept : CEO, salary :30000 ]
Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
Employee :[ Name : Richard, dept : Sales, salary :10000 ]
Employee :[ Name : Rob, dept : Sales, salary :10000 ]
Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
Employee :[ Name : Bob, dept : Marketing, salary :10000 ]
进程完成,退出码 0
**备忘录模式(Memento Pattern)**保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
介绍
**意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
**主要解决:**所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
何时使用:很多时候总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先得状态,使得他有“后悔药”可吃。
如何解决:通过一个备忘录类专门存储对象状态。
关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。
应用实例:
ctrl
+z。优点:
缺点:
使用场景:
注意事项:
实现
备忘录模式使用三个类Memento
、Originator
和CareTaker
。Memento
包含了要被恢复的对象的状态。Originator
创建并在Memento
对象中存储状态。Caretaker
对象负责从Memento
中恢复对象的状态。演示类MementoPatternDemo
使用CareTaker
和Originator
对象来显示对象的状态恢复。
步骤一
创建Memento类:Memento.java
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
步骤二
创建Originator类:Originator.java
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
步骤三
创建CareTaker
类:CareTaker.java
public class CareTaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
步骤四
使用CareTaker
和Originator
对象:MementoPatternDemo.java
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: "+originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: "+originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: "+originator.getState());
}
}
结果
Current State: State #4
First saved State: State #2
Second saved State: State #3
进程完成,退出码 0
在**策略模式(Strategy Pattern)**中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,创建表示各种策略的对象和一个行为随着策略对象而改变的context对象。策略对象改变context对象的执行算法。
介绍
意图:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
主要解决:在有多种算法相似的情况下,使用if…else所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
应用实例:
JAVA AWT
中的LayoutManager
优点:
缺点:
使用场景:
注意事项:
实现
创建一个定义活动的Strategy接口和实现了Strategy接口的实体策略类。Context是一个使用了某种策略的类。演示类StrategyPatternDemo
使用Context和策略对象来演示Context在它所配置或使用的策略改变时的行为变化。
步骤一
创建一个接口:Strategy.java
public interface Strategy {
public int doOperation(int num1, int num2);
}
步骤二
创建实现接口的实体类:OperationAdd.java
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
OperationSubtract.java
public class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
OperationMultiply.java
public class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
步骤三
创建context类:Context.java
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
步骤四
使用Context来查看当它改变策略Strategy时的行为变化:StrategyPatternDemo.java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = "+context.executeStrategy(10,5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = "+context.executeStrategy(10,5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = "+context.executeStrategy(10,5));
}
}
结果
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
进程完成,退出码 0
**迭代器模式(Iterator Pattern)**时Java和.Net编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式属于行为型模式
介绍
意图:提供一种方法顺序访问一个聚合对象中各个元素,而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext
,next。
应用实例:Java中的iterator。
优点:
缺点:
使用场景:
注意事项:
实现
创建一个叙述导航方法的Iterator接口和一个返回迭代器的Container接口,实现了Container接口的实体类将负责实现Iterator接口。演示类IteratorPatternDemo
使用实体类NamesRepository
来打印NamesRepository
中存储为集合的Names。
步骤一
创建接口:Iterator.java
public interface Iterator {
public boolean hasNext();
public Object next();
}
Container.java
public interface Container {
public Iterator getIterator();
}
步骤二
创建实现了Container接口的实体类。该类有实现了Iterator接口的内部类NameIterator
:NameRepository.java
public class NameRepository implements Container{
public String names[] = {"Robert", "John", "Julie", "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if (index < names.length) {
return true;
}
return false;
}
@Override
public Object next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
}
步骤三
使用NameRepository
来获取迭代器,并打印名字:IteratorPatternDemo.java
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository nameRepository = new NameRepository();
for (Iterator iter = nameRepository.getIterator(); iter.hasNext();) {
String name = (String) iter.next();
System.out.println("Name : "+name);
}
}
}
结果
Name : Robert
Name : John
Name : Julie
Name : Lora
进程完成,退出码 0
在**访问者模式(Visitor Pattern)**中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
介绍
意图:主要讲数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接收访问者,将自身引用传入访问者。
应用实例:
优点:
缺点:
使用场景:
注意事项:
UI
、拦截器与过滤器。实现
创建一个定义接受操作的ComputerPart
接口。Keyboard
、Mouse
、Monitor
和Computer
是实现了ComputerPart
接口的实体类。定义另一个接口ComputerPartVisitor
,它定义了访问者类的操作。Computer使用实体访问者来执行相应的动作。通过演示类VisitorPatternDemo
的使用Computer
、ComputerPartVisitor
类来演示访问者模式的用法。
步骤一
定义一个表示元素的接口:ComputerPart.java
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
步骤二
创建扩展了上述类的实体类:Keyboard.java
public class Keyboard implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Monitor.java
public class Monitor implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Mouse.java
public class Mouse implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Computer.java
public class Computer implements ComputerPart{
ComputerPart[] parts;
public Computer() {
parts = new ComputerPart[]{new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
步骤三
定义一个表示访问者的接口:ComputerPartVisitor.java
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
步骤四
创建实现了上述类的实体访问者:ComputerPartDisplayVisitor.java
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer");
}
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor");
}
}
步骤五
使用ComputerPartDisplayVisitor
来显示Computer的组成部分:VisitorPatternDemo.java
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}
结果
Displaying Mouse
Displaying Keyboard
Displaying Monitor
Displaying Computer
进程完成,退出码 0
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个ArrayList
存放观察者们。
应用实例:
优点:
缺点:
使用场景:
注意事项:
实现:
观察者模式使用三个类Subject
(被观察者)、Observer
(观察者)和Client
。Subject
对象带有绑定观察者到Client
对象和从Client
对象解绑观察者的方法。创建Subject
类、Observer
抽象类和扩展了抽象类Observer
的实体类。演示类ObserverPatternDemo
使用Subject和实体类对象来演示观察者模式。
步骤1
创建Subject类:Subject.java
public class Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer) {
observers.add(observer);
}
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
步骤2
创建Observer类:Observer.java
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
步骤3
创建实体观察者类:BinaryObserver.java
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Binary String: "+Integer.toBinaryString(subject.getState()));
}
}
OctalObserver.java
public class OctalObserver extends Observer{
public OctalObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Octal String: "
+Integer.toOctalString(subject.getState()));
}
}
HexaObserver.java
public class HexaObserver extends Observer{
public HexaObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Hex String: "
+Integer.toHexString(subject.getState()).toUpperCase());
}
}
步骤4
使用Subject和实体观察者对象:ObserverPatternDemo.java
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new BinaryObserver(subject);
new HexaObserver(subject);
new OctalObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println();
System.out.println("Second state change: 10");
subject.setState(10);
}
}
步骤5
结果:
First state change: 15
Binary String: 1111
Hex String: F
Octal String: 17
Second state change: 10
Binary String: 1010
Hex String: A
Octal String: 12
Process finished with exit code 0
**解释器模式(Interpreter Pattern)**提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在SQL
解析、符号处理引擎等。
介绍
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决:对于一些固定文法构建一个解释句子的解释器。
何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决:构建语法树,定义终结符与非终结符。
关键代码:构建环境类,包含解释器之外的一些全局信息,一般是HashMap
。
应用实例:编译器、运算表达式计算。
优点:
缺点:
使用场景:
注意事项:
expression4J
代替。实现
创建一个接口Expression
和实现了Expression
接口的实体类。定义作为上下文中主要解释器的TerminalExpression
类。其他的类OrExpression
、AndExpression
用于创建组合式表达式。演示类InterpreterPatternDemo
使用Expression
类创建规则和演示表达式的解析。
步骤一
创建一个表达式接口:Expression.java
public interface Expression {
public boolean interpret(String context);
}
步骤二
创建实现了上述接口的实体类:TerminalExpression.java
public class TerminalExpression implements Expression{
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
if (context.contains(data)) {
return true;
}
return false;
}
}
OrExpression.java
public class OrExpression implements Expression{
private Expression expr1 = null;
private Expression expr2 = null;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
AndExpression.java
public class AndExpression implements Expression{
private Expression expr1 = null;
private Expression expr2 = null;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
步骤三
InterpreterPatternDemo
使用Expression
类来创建规则,并解析它们:InterpreterPatternDemo.java
public class InterpreterPatternDemo {
//规则:Robert和John是男性
public static Expression getMaleExpression() {
Expression robert = new TerminalExpression("Robert");
Expression john = new TerminalExpression("John");
return new OrExpression(robert, john);
}
//规则:Juile是一个已婚的女性
public static Expression getMarrieldExpression() {
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarrieldExpression();
System.out.println("John is male? "+isMale.interpret("John"));
System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie"));
}
}
结果
John is male? true
Julie is a married women? true
进程完成,退出码 0
在**空对象模式(Null Object Pattern)**中,一个空对象取代NULL对象实例的检查。Null对象不是检查空值,而是反应一个不做任何动作的关系。这样的Null对象也可以在数据不可用的时候提供默认的行为。
在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。
实现
创建一个定义操作(在这里,是客户的名称)的AbstractCustomer
抽象类,和扩展了AbstractCustomer
类的实体类。工厂类CustomerFactory
基于客户传递的名字来返回RealCustomer
或NullCustomer
对象。演示类NullPatternDemo
使用CustomerFactory
来演示空对象模式的用法。
步骤一
创建一个抽象类:AbstractCustomer.java
public abstract class AbstractCustomer {
protected String name;
public abstract boolean isNil();
public abstract String getName();
}
步骤二
创建扩展了上述类的实体类:RealCustomer.java
public class RealCustomer extends AbstractCustomer{
public RealCustomer(String name) {
this.name = name;
}
@Override
public boolean isNil() {
return false;
}
@Override
public String getName() {
return name;
}
}
NullCustomer.java
public class NullCustomer extends AbstractCustomer{
@Override
public boolean isNil() {
return true;
}
@Override
public String getName() {
return "Not Available in Customer Database";
}
}
步骤三
创建CustomerFactory
类。
public class CustomerFactory {
public static final String[] names = {"Rob", "Joe", "Julie"};
public static AbstractCustomer getCustomer(String name) {
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase(name)) {
return new RealCustomer(name);
}
}
return new NullCustomer();
}
}
步骤四
使用CustomerFactory
,基于客户传递的名字,来获取RealCustomer
或NullCustomer
对象:NullPatternDemo.java
public class NullPatternDemo {
public static void main(String[] args) {
AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob");
AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob");
AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie");
AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura");
System.out.println("Customers : ");
System.out.println(customer1.getName());
System.out.println(customer2.getName());
System.out.println(customer3.getName());
System.out.println(customer4.getName());
}
}
结果:
Customers :
Rob
Not Available in Customer Database
Julie
Not Available in Customer Database
进程完成,退出码 0
**命令模式(Command Pattern)**是一种数据驱动型的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
介绍
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。
关键代码:定义三个角色:1、received真正的命令执行对象。2、Command。3、invoker使用命令对象的入口。
应用实例:struts1
中的action
核心控制器ActionServlet
只有一个,相当于invoker
,而模型层的类会随着不同的应用有不同的模型类,相当于具体的Command
。
优点:
缺点:
使用场景:
GUI
中每一个按钮都是一条命令。2、模拟CMD
。注意事项:
命令模式结构示意图:
实现
首先创建作为命令的接口Order,然后创建作为请求的Stock类。实体命令类BuyStock
和SellStock
,实现了Order接口,将执行实际的命令处理。创建作为调用对象的类Broker,它接受订单并能下订单。
Broker对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。CommandPatternDemo
,演示类使用Broker类来演示命令模式。
步骤一
创建一个命令接口:Order.java
public interface Order {
void execute();
}
步骤二
创建一个请求类:Stock.java
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy() {
System.out.println("Stock [ Name: "+name+",Quantity: "+quantity+" ] bought");
}
public void sell() {
System.out.println("Stock [ Name: "+name+",Quantity: "+quantity+" ] sold");
}
}
步骤三
创建实现了Order接口的实体类:BuyStock.java
public class BuyStock implements Order{
private Stock stock;
public BuyStock(Stock stock) {
this.stock = stock;
}
@Override
public void execute() {
stock.buy();
}
}
SellStock.java
public class SellStock implements Order {
private Stock stock;
public SellStock(Stock stock) {
this.stock = stock;
}
@Override
public void execute() {
stock.sell();
}
}
步骤四
创建命令调用类:Broker.java
public class Broker {
private List<Order> orderList = new ArrayList<>();
public void takeOrder(Order order) {
orderList.add(order);
}
public void placeOrders() {
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
步骤五
使用Broker类来接受并执行命令:CommandPatternDemo.java
public class CommandPatternDemo {
public static void main(String[] args) {
Stock stock = new Stock();
BuyStock buyStockOrder = new BuyStock(stock);
SellStock sellStock = new SellStock(stock);
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStock);
broker.placeOrders();
}
}
结果
Stock [ Name: ABC,Quantity: 10 ] bought
Stock [ Name: ABC,Quantity: 10 ] sold
进程完成,退出码 0
在**模板模式(Template Pattern)**中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
介绍
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例:
优点:
缺点:
使用场景:
注意事项:
实现
创建一个定义操作的Game抽象类,其中,模板方法设置为final,这样它就不会被重写。Cricket和Football是扩展了Game的实体类,它们重写了抽象类的方法。TemplatePatternDemo
使用Game来演示模板模式的用法。
步骤一
创建一个抽象类,它的模板方法被设置为final:Game.java
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play() {
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
步骤二
创建扩展了上述类的实体类:Cricket.java
public class Cricket extends Game{
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
}
Football.java
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
步骤三
使用Game的模板方法play()来演示游戏的定义方式:TemplatePatternDemo.java
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
结果
Cricket Game Initialized! Start playing
Cricket Game Started. Enjoy the game!
Cricket Game Finished!
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
进程完成,退出码 0
顾名思义,**责任链模式(Chain of Responsibility Pattern)**为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么会把相同的请求传给下一个接收者,以此类推。
介绍
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理中解耦了。
何时使用:在处理消息的时候已过滤很多道。
如何解决:拦截的类都实现统一接口。
关键代码:Handler里面聚合它自己,在HandlerRequest
里判断是否合适,如果没达到条件则向下传递,向谁传递之前set进去。
应用实例:
JS
中的事件冒泡。JAVA WEB
中Apache Tomcat
对Encoding
的处理,Struts2
的拦截器,jsp servlet
的Filter。优点:
缺点:
使用场景:
注意事项:
实现
创建抽象类AbstractLogger
,带有详细的日志记录级别。然后创建三种类型的记录器,都扩展了AbstractLogger
。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
步骤一
创建抽象的记录器类:AbstractLogger.java
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
//责任链中的下一个元素
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message) {
if (this.level <= level) {
write(message);
}
if (nextLogger != null) {
nextLogger.logMessage(level,message);
}
}
abstract protected void write(String message);
}
步骤二
创建扩展了该记录器类的实体类:ConsoleLogger.java
public class ConsoleLogger extends AbstractLogger{
public ConsoleLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: "+message);
}
}
ErrorLogger.java
public class ErrorLogger extends AbstractLogger{
public ErrorLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: "+message);
}
}
FileLogger.java
public class FileLogger extends AbstractLogger{
public FileLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("File::Logger: "+message);
}
}
步骤三
创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分:ChainPatternDemo.java
public class ChainPatternDemo {
private static AbstractLogger getChainOfLoggers() {
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO,"This is an information");
loggerChain.logMessage(AbstractLogger.DEBUG, "This is an debug level information");
loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information");
}
}
结果
Standard Console::Logger: This is an information
File::Logger: This is an debug level information
Standard Console::Logger: This is an debug level information
Error Console::Logger: This is an error information
File::Logger: This is an error information
Standard Console::Logger: This is an error information
进程完成,退出码 0
**中介者模式(Mediator Pattern)**是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
介绍
意图:用一个中介对象来封装系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。
关键代码:对象Colleague之间的通信封装道一个类中单独处理。
应用实例:
MVC
框架,其中C(控制器)就是M(模型)和V(视图)的中介者。优点:
缺点:
使用场景:
注意事项:
实现
通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。创建两个类ChatRoom
和User
。User
对象使用ChatRoom
方法来分享他们的消息。演示类MediatorPatternDemo
使用User对象来显示他们之间的通信。
步骤一
创建中介类:ChatRoom.java
public class ChatRoom {
public static void showMessage(User user, String message) {
System.out.println(new Date().toString()
+" ["+user.getName()+"] : "+message);
}
}
步骤二
创建User类:User.java
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
this.name = name;
}
public void sendMessage(String message) {
ChatRoom.showMessage(this,message);
}
}
步骤三
使用User对象来显示他们之间的通信:MediatorPatternDemo.java
public class MediatorPatternDemo {
public static void main(String[] args) {
User robert = new User("Robert");
User john = new User("John");
robert.sendMessage("Hi!John!");
john.sendMessage("Hi!Robert!");
}
}
结果
Mon Aug 17 17:07:08 CST 2020 [Robert] : Hi!John!
Mon Aug 17 17:07:08 CST 2020 [John] : Hi!Robert!
进程完成,退出码 0
在**状态模式(State Pattern)**中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,创建表示各种状态的对象和一个行为随着状态对象改变而改变的context对象。
介绍
意图:允许对象在内部状态发送改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除if…else等条件选择语句。
应用实例:
优点:
缺点:
使用场景:
注意事项:
实现
创建一个State
接口和实现了State
接口的实体状态类。Context
是一个带有某个状态的类。演示类StatePatternDemo
使用Context
和状态对象来演示Context
在状态改变时的行为变化。
步骤一
创建一个接口:State.java
public interface State {
public void doAction(Context context);
}
步骤二
创建实现接口的实体类:StartState.java
public class StartState implements State{
@Override
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
StopState.java
public class StopState implements State{
@Override
public void doAction(Context context) {
System.out.println("Player is in Stop state");
context.setState(this);
}
@Override
public String toString() {
return "Stop State";
}
}
步骤三
创建Context类:Context.java
public class Context {
private State state;
public Context() {
state = null;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
步骤四
使用Context来查看当前状态State改变时的行为变化:StatePatternDemo.java
public class StartPatternDemo {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState().toString());
StopState stopState = new StopState();
stopState.doAction(context);
System.out.println(context.getState().toString());
}
}
结果
Player is in start state
Start State
Player is in Stop state
Stop State
进程完成,退出码 0
介绍
MVC模式代表Model-View-Controller(模型-视图-控制器)模式。这种模式用于应用程序的分层开发。
Model(模型)-模型代表一个存取数据的对象或JAVA POJO
。它可以带有逻辑,在数据变化时更新控制器。
View(视图)-视图代表模型包含的数据的可视化。
Controller(控制器)-控制器作用于模型图和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
实现
创建一个作为模型的Student对象。StudentView
是一个把学生详细信息输出到控制台的视图类,StudentController
是负责存储数据到Student对象中的控制器类,并相应地更新视图StudentView
。
MVCPatternDemo
,我们的演示类使用StudentController
来演示MVC
模式的用法。
步骤一
创建模式:Student.java
public class Student {
private String rollNo;
private String name;
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
步骤二
创建视图:StudentView.java
public class StudentView {
public void printStudentDetails(String studentName, String studentRollNo) {
System.out.println("Student: ");
System.out.println("Name: "+studentName);
System.out.println("Roll No: "+studentRollNo);
}
}
步骤三
创建控制器:StudentController.java
public class StudentController {
private Student model;
private StudentView view;
public StudentController(Student model, StudentView view) {
this.model = model;
this.view = view;
}
public String getRollNo() {
return model.getRollNo();
}
public void setRollNo(String rollNo) {
model.setRollNo(rollNo);
}
public String getName() {
return model.getName();
}
public void setName(String name) {
model.setName(name);
}
public void updateView() {
view.printStudentDetails(model.getName(),model.getRollNo());
}
}
步骤四
使用StudentController
方法来演示MVC设计模式的用法:MVCPatternDemo.java
public class MVCPatterDemo {
public static void main(String[] args) {
//从数据库获取学生记录
Student model = retrieveStudentFromDatabase();
//创建一个视图:把学生详细信息输出到控制台
StudentView view = new StudentView();
StudentController controller = new StudentController(model, view);
controller.updateView();
//更新模型数据
controller.setName("John");
controller.updateView();
}
private static Student retrieveStudentFromDatabase() {
Student student = new Student();
student.setName("Robert");
student.setRollNo("10");
return student;
}
}
结果:
Student:
Name: Robert
Roll No: 10
Student:
Name: John
Roll No: 10
进程完成,退出码 0
**传输对象模式(Transfer Object Pattern)**用于从客户端向服务器一次性传递带有多个属性的数据。传输对象也被称为数值对象。传输对象是一个具有getter/setter方法的简单的POJO
类,它是可以序列化的,所以通过网络传输。它没有任何的行为。服务器端的业务类通常从数据库读取数据,然后填充POJO
,并把它发送到客户端或按值传递它。对于客户端,传输对象是只读的。客户端可以创建自己的传输对象,并把它传递给服务器,以便一次性更新数据库中的数值。以下是这种设计模式的实体。
POJO
,只有设置/获取属性的方法。实现
创建一个作为业务对象的StudentBO
和作为传输对象的StudentVO
,它们都代表了实体。演示类TransferObjectPatternDemo
在这里是作为一个客户端,将使用StudentBO
和Student来演示传输对象设计模式。
步骤一
创建传输对象:StudentVO.java
public class StudentVO {
private String name;
private int rollNo;
public StudentVO(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRollNo() {
return rollNo;
}
public void setRollNo(int rollNo) {
this.rollNo = rollNo;
}
}
步骤二
创建业务对象:StudentBO.java
public class StudentBO {
//列表是当作一个数据库
List<StudentVO> students;
public StudentBO() {
students = new ArrayList<>();
StudentVO student1 = new StudentVO("Robert",0);
StudentVO student2 = new StudentVO("John", 1);
students.add(student1);
students.add(student2);
}
public void deleteStudent(StudentVO student) {
students.remove(student.getRollNo());
System.out.println("Student: Roll NO "+
student.getRollNo()+", deleted from database");
}
//从数据库中检索学生名单
public List<StudentVO> getAllStudents() {
return students;
}
public StudentVO getStudent(int rollNo) {
return students.get(rollNo);
}
public void updateStudent(StudentVO student) {
students.get(student.getRollNo()).setName(student.getName());
System.out.println("Student: Roll No "
+student.getRollNo()+", updated in the database");
}
}
步骤三
使用StudentBO
来演示传输对象设计模式:TransferObjectPatternDemo.java
public class TransferObjectPatternDemo {
public static void main(String[] args) {
StudentBO studentBusinessObject = new StudentBO();
//输出所有学生
for (StudentVO studentVO : studentBusinessObject.getAllStudents()) {
System.out.println("Student: [RollNo : "
+ studentVO.getRollNo() + ", Name : " + studentVO.getName() + " ]");
}
//更新学生
StudentVO student = studentBusinessObject.getAllStudents().get(0);
student.setName("Michael");
studentBusinessObject.updateStudent(student);
//获取学生
studentBusinessObject.getStudent(0);
System.out.println("Student: [RollNo : "
+ student.getRollNo() + ", Name : " + student.getName() + " ]");
}
}
结果
Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]
进程完成,退出码 0
**服务定位器模式(Service Locator Pattern
)**用在我们想使用JNDI
查询定位各种服务的时候。考虑到为某个服务查找JNDI
的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在JNDI
中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。以下是这种设计模式的主体。
JNDI
服务器中查找到。JNDI Contex
t带有对要查找的服务的引用。Service Locator
):服务定位器是通过JNDI
查找和缓存服务来获取服务的单点接触。ServiceLocator
调用服务的对象。实现
创建ServiceLocator
、InitialContext
、Cache、Service作为表示实体的各种对象。Service1
和Service2
表示各种实体服务。演示类ServiceLocatorPatternDemo
在这里作为一个客户端,将使用ServiceLocator
来演示服务定位器设计模式。
步骤一
创建服务接口:Service.java
public interface Service {
public String getName();
public void execute();
}
步骤二
创建实体服务(主要表现接口之间的多态性,指定行为方式):Service1.java
public class Service1 implements Service{
@Override
public String getName() {
return "Service1";
}
@Override
public void execute() {
System.out.println("Executing Service1");
}
}
Service2.java
public class Service2 implements Service{
@Override
public String getName() {
return "Service2";
}
@Override
public void execute() {
System.out.println("Executing Service2");
}
}
步骤三
为JNDI
查询创建InitialContext
(也就是工厂模式的应用,通过类名来确定要实例化的对象):InitialContext.java
public class InitialContext {
public Object lookup(String jndiName) {
if (jndiName.equalsIgnoreCase("SERVICE1")) {
System.out.println("Looking up and creating a new Service1 Object");
return new Service1();
} else if (jndiName.equalsIgnoreCase("SERVICE2")) {
System.out.println("Looking up and creating a new Service2 Object");
return new Service2();
}
return null;
}
}
步骤四
创建缓存Cache(用的是传输对象模式。对实体类集合进行操作,主要是在集合中获取/添加实体类对象):Cache.java
public class Cache {
private List<Service> services;
public Cache() {
services = new ArrayList<>();
}
public Service getService(String serviceName) {
for (Service service : services) {
if (service.getName().equalsIgnoreCase(serviceName)) {
System.out.println("Returning cached "+serviceName+" object");
return service;
}
}
return null;
}
public void addService(Service newService) {
boolean exists = false;
for (Service service : services) {
if (service.getName().equalsIgnoreCase(newService.getName())) {
exists = true;
}
}
if (!exists) {
services.add(newService);
}
}
}
步骤五
创建服务定位器(定位器:使用步骤3来创建实例,使用步骤4来添加到集合,或者从集合中获取。(缓存中没有才会创建)):ServiceLocator.java
public class ServiceLocator {
private static Cache cache;
static {
cache = new Cache();
}
public static Service getService(String jndiName) {
Service service = cache.getService(jndiName);
if (service != null) {
return service;
}
InitialContext context = new InitialContext();
Service newService = (Service) context.lookup(jndiName);
cache.addService(newService);
return newService;
}
}
步骤六
使用ServiceLocator
来演示服务定位器设计模式(调用步骤5得到实体类,并执行实体类的方法):ServiceLocatorPatternDemo.java
public class ServiceLocatorPatternDemo {
public static void main(String[] args) {
Service service = ServiceLocator.getService("Service1");
service.execute();
service = ServiceLocator.getService("Service2");
service.execute();
service = ServiceLocator.getService("Service1");
service.execute();
service = ServiceLocator.getService("Service2");
service.execute();
}
}
结果
Looking up and creating a new Service1 Object
Executing Service1
Looking up and creating a new Service2 Object
Executing Service2
Returning cached Service1 object
Executing Service1
Returning cached Service2 object
Executing Service2
进程完成,退出码 0
**拦截过滤器模式(Intercepting Filter Pattern)**用于对应用程序的请求或响应做一些预处理/后处理。定义过滤器,并在把请求传给实际目标应用程序之前应用在请求上。过滤器可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。
Target
对象是请求处理程序。实现
创建FilterChain
、FilterManager
、Target、Client作为表示实体的各种对象。AuthenticationFilter
和DebugFilter
表示实体过滤器。演示类InterceptingFilterDemo
使用Client来演示拦截过滤器设计模式。
步骤一
创建过滤器接口Filter:Filter.java
public interface Filter {
public void execute(String request);
}
步骤二
创建实体过滤器:AuthenticationFilter.java
public class AuthenticationFilter implements Filter{
@Override
public void execute(String request) {
System.out.println("Authenticating request: " + request);
}
}
DebugFilter.java
public class DebugFilter implements Filter{
@Override
public void execute(String request) {
System.out.println("request log: " + request);
}
}
步骤三
创建Target:Target.java
public class Target {
public void execute(String request) {
System.out.println("Executing request: " + request);
}
}
步骤四
创建过滤器链:FilterChain.java
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private Target target;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void execute(String request) {
for (Filter filter : filters) {
filter.execute(request);
}
target.execute(request);
}
public void setTarget(Target target) {
this.target = target;
}
}
步骤五
创建过滤管理器:FilterManager.java
public class FilterManager {
FilterChain filterChain;
public FilterManager(Target target) {
filterChain = new FilterChain();
filterChain.setTarget(target);
}
public void setFilter(Filter filter) {
filterChain.addFilter(filter);
}
public void filterRequest(String request) {
filterChain.execute(request);
}
}
步骤六
创建客户端Client:Client.java
public class Client {
FilterManager filterManager;
public void setFilterManager(FilterManager filterManager) {
this.filterManager = filterManager;
}
public void sendRequest(String request) {
filterManager.filterRequest(request);
}
}
步骤七
使用Client来演示拦截过滤器设计模式:InterceptingFilterChainDemo.java
public class InterceptingFilterDemo {
public static void main(String[] args) {
FilterManager filterManager = new FilterManager(new Target());
filterManager.setFilter(new AuthenticationFilter());
filterManager.setFilter(new DebugFilter());
Client client = new Client();
client.setFilterManager(filterManager);
client.sendRequest("HOME");
}
}
结果
Authenticating request: HOME
request log: HOME
Executing request: HOME
进程完成,退出码 0
**前端控制器模式(Front Controller Pattern)**是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。以下是这种设计模式的实体。
实现
创建FrontController
、Dispatcher
分别当作前端控制器和调度器。HomeView
和StudentView
表示各种为前端控制器接收到的请求而创建的视图。演示类FrontControllerPatternDemo
使用FrontController
来演示前端控制器设计模式。
步骤一
创建视图:HomeView.java
public class HomeView {
public void show() {
System.out.println("Displaying Home Page");
}
}
StudentView.java
public class StudentView{
public void show() {
System.out.println("Displaying Student Page");
}
}
步骤二
创建调度器Dispatcher:Dispatcher.java
public class Dispatcher {
private StudentView studentView;
private HomeView homeView;
public Dispatcher() {
studentView = new StudentView();
homeView = new HomeView();
}
public void dispatcher(String request) {
if (request.equalsIgnoreCase("STUDENT")) {
studentView.show();
} else {
homeView.show();
}
}
}
步骤三
创建前端控制器FrontController
:FrontController.java
public class FrontController {
private Dispatcher dispatcher;
public FrontController() {
dispatcher = new Dispatcher();
}
private boolean isAuthenticUser() {
System.out.println("User is authenticated successfully");
return true;
}
private void trackRequest(String request) {
System.out.println("Page request: " + request);
}
public void dispatchRequest(String request) {
//记录每一个请求
trackRequest(request);
//对用户进行身份验证
if (isAuthenticUser()) {
dispatcher.dispatcher(request);
}
}
}
步骤四
使用FrontController
来演示前端控制器设计模式:FrontControllerPatternDemo.java
public class FrontControllerPatternDemo {
public static void main(String[] args) {
FrontController frontController = new FrontController();
frontController.dispatchRequest("HOME");
frontController.dispatchRequest("STUDENT");
}
}
结果
Page request: HOME
User is authenticated successfully
Displaying Home Page
Page request: STUDENT
User is authenticated successfully
Displaying Student Page
进程完成,退出码 0
**数据对象访问模式(Data Access Object Pattern)**或DAO
模式用于把低级别的数据访问API
或操作从高级的业务服务中分离出来。以下是数据访问对象模式的参与者。
XML
,或者是其他的存储机制。POJO
,包含了get/set方法来存储通过使用DAO
类检索到的数据。实现
创建一个作为模型对象或数值对象的Student对象。StudentDao
是数据访问对象接口。StudentDaoImpl
是实现了数据访问对象接口的实体类。演示类DaoPatternDemo
使用StudentDao
来演示数据访问对象模式的用法。
步骤一
创建数值对象:Student.java
public class Student {
private String name;
private int rollNo;
Student(String name, int rollNo){
this.name = name;
this.rollNo = rollNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRollNo() {
return rollNo;
}
public void setRollNo(int rollNo) {
this.rollNo = rollNo;
}
}
步骤二
创建数据访问对象接口:StudentDao.java
public interface StudentDao {
public List<Student> getAllStudents();
public Student getStudent(int rollNo);
public void updateStudent(Student student);
public void deleteStudent(Student student);
}
步骤三
创建实现了上述接口的实体类:StudentDaoImpl.java
public class StudentDaoImpl implements StudentDao {
//列表是当作一个数据库
List<Student> students;
public StudentDaoImpl(){
students = new ArrayList<Student>();
Student student1 = new Student("Robert",0);
Student student2 = new Student("John",1);
students.add(student1);
students.add(student2);
}
@Override
public void deleteStudent(Student student) {
students.remove(student.getRollNo());
System.out.println("Student: Roll No " + student.getRollNo()
+", deleted from database");
}
//从数据库中检索学生名单
@Override
public List<Student> getAllStudents() {
return students;
}
@Override
public Student getStudent(int rollNo) {
return students.get(rollNo);
}
@Override
public void updateStudent(Student student) {
students.get(student.getRollNo()).setName(student.getName());
System.out.println("Student: Roll No " + student.getRollNo()
+", updated in the database");
}
}
步骤四
使用StudentDao
来演示数据访问对象模式的用法:DaoPatternDemo.java
public class DaoPatternDemo {
public static void main(String[] args) {
StudentDao studentDao = new StudentDaoImpl();
//输出所有的学生
for (Student student : studentDao.getAllStudents()) {
System.out.println("Student: [RollNo : "
+ student.getRollNo() + ", Name : " + student.getName() + " ]");
}
//更新学生
Student student = studentDao.getAllStudents().get(0);
student.setName("Michael");
studentDao.updateStudent(student);
//获取学生
studentDao.getStudent(0);
System.out.println("Student: [RollNo : "
+ student.getRollNo() + ", Name : " + student.getName() + " ]");
}
}
结果
Student: [RollNo : 0, Name : Robert ]
Student: [RollNo : 1, Name : John ]
Student: Roll No 0, updated in the database
Student: [RollNo : 0, Name : Michael ]
进程完成,退出码 0
业务代表模式(Business Delegate Pattern)用于对表示层和业务层解耦。它基本上是用来减少通信或对表示层代码中的业务层代码的远程查询功能。在业务层中有以下实体。
Client
):表示层代码可以是JSP
、servlet
或UI java
代码Business Delegate
):一个为客户端实体提供的入口类,它提供了对业务服务方法的访问。LookUp Service
):查找服务对象负责获取相关的业务实现,并提供业务对象对业务代表对象的访问。Business Service
):业务服务接口。实现了该业务服务的实体类,提供了实际的业务实现逻辑。实现
将创建 Client、BusinessDelegate
、BusinessService
、LookUpService
、JMSService
和 EJBService
来表示业务代表模式中的各种实体。演示类BusinessDelegatePatternDemo
使用 BusinessDelegate
和 Client
来演示业务代表模式的用法。
步骤一
创建BusinessService
接口:BusinessService.java
public interface BusinessService {
public void doProcessing();
}
步骤二
创建实体服务类:EJBService.java
public class EJBService implements BusinessService{
@Override
public void doProcessing() {
System.out.println("Processing task by invoking EJB Service");
}
}
JMSService.java
public class JMSService implements BusinessService{
@Override
public void doProcessing() {
System.out.println("Processing task by invoking JMS Service");
}
}
步骤三
创建业务查询服务:BusinessLookUp.java
public class BusinessLookUp {
public BusinessService getBusinessService(String serviceType) {
if (serviceType.equals("EJB")) {
return new EJBService();
} else {
return new JMSService();
}
}
}
步骤四
创建业务代表:BusinessDelegate.java
public class BusinessDelegate {
private BusinessLookUp lookUpService = new BusinessLookUp();
private BusinessService businessService;
private String serviceType;
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
}
public void doTask() {
businessService = lookUpService.getBusinessService(serviceType);
businessService.doProcessing();
}
}
步骤五
创建客户端:Client.java
public class Client {
BusinessDelegate businessService;
public Client(BusinessDelegate businessService) {
this.businessService = businessService;
}
public void doTask() {
businessService.doTask();
}
}
步骤六
使用BusinessDelegate
和Client
类来演示业务代表模式:BusinessDelegatePatternDemo.java
public class BusinessDelegatePatternDemo {
public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType("EJB");
Client client = new Client(businessDelegate);
client.doTask();
businessDelegate.setServiceType("JMS");
client.doTask();
}
}
结果
Processing task by invoking EJB Service
Processing task by invoking JMS Service
进程完成,退出码 0
**组合实体模式(Composite Entity Pattern)**用在EJB
持久化机制中。一个组合实体是一个EJB
实体bean,代表了对象的图解。当更新一个组合实体时,内部依赖对象beans会自动更新,因为它们是由EJB
实体bean管理的。以下是组合实体bean的参与者。
实现
创建作为组合实体的CompositeEntity
对象。CoarseGrainedObject
是一个包含依赖对象的类。演示类CompositeEntityPattenDemo
使用Client
类来演示组合实体模式的用法。
步骤一
创建依赖对象:DependentObject1.java
public class DependentObject1 {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
DependentObject2.java
public class DependentObject2 {
private String data;
public void setData(String data){
this.data = data;
}
public String getData(){
return data;
}
}
步骤二
创建粗粒度对象:CoarseGrainedObject.java
public class CoarseGrainedObject {
DependentObject1 dependentObject1 = new DependentObject1();
DependentObject2 dependentObject2 = new DependentObject2();
public void setData(String data1, String data2) {
dependentObject1.setData(data1);
dependentObject2.setData(data2);
}
public String[] getData() {
return new String[]{dependentObject1.getData(), dependentObject2.getData()};
}
}
步骤三
创建组合实体:CompositeEntity.java
public class CompositeEntity {
private CoarseGrainedObject coarseGrainedObject = new CoarseGrainedObject();
public void setData(String data1, String data2) {
coarseGrainedObject.setData(data1, data2);
}
public String[] getData() {
return coarseGrainedObject.getData();
}
}
步骤四
创建使用组合实体的客户端类:Client.java
public class Client {
private CompositeEntity compositeEntity = new CompositeEntity();
public void printData() {
for (int i = 0; i < compositeEntity.getData().length; i++) {
System.out.println("Data: "+compositeEntity.getData()[i]);
}
}
public void setData(String data1, String data2) {
compositeEntity.setData(data1, data2);
}
}
步骤五
使用Client来演示组合实体设计模式的用法:CompositeEntityPatternDemo.java
public class CompositeEntityPatternDemo {
public static void main(String[] args) {
Client client = new Client();
client.setData("Test","Data");
client.printData();
client.setData("Second Test","Data1");
client.printData();
}
}
结果
Data: Test
Data: Data
Data: Second Test
Data: Data1
进程完成,退出码 0