前言
这篇文章可以说是java基础的范畴,为了下一篇Android开发中的常用设计模式做一下铺垫,也顺便反思一下。
正文
设计模式分类
分类方式是多样的,这里我们按照功能类型进行分类:
创建型
- 工厂方法 Factory Method
- 抽象工厂 Abstract Factory
- 建造者 Builder
- 原型 Prototype
- 单例 Singleton
结构型
- 适配器 Adapter Class/Object
- 桥接 Bridge
- 组合 Composite
- 装饰 Decorator
- 外观 Facade
- 享元 Flyweight
- 代理 Proxy
行为型
- 解释器 Interpreter
- 模板方法 Template Method
- 责任链 Chain of Responsibility
- 命令 Command
- 迭代器 Iterator
- 中介者 Mediator
- 备忘录 Memento
- 观察者 Observer
- 状态 State
- 策略 Strategy
- 访问者 Visitor
一共23种设计模式。
设计模式概述
创建型
创建型的五种设计模式,其目的都是为了创建对象(生成实例)
工厂方法
简单工厂模式就不谈了,工厂方法模式的UML图:
工厂必然是用来流水化生产的,“风格迥异”的产品不适合使用工厂模式
工厂方法模式与简单工厂模式不同的是:将原有的工厂类,划分为 抽象工厂 和 具体工厂 。其中抽象工厂提供了一个接口,具体工厂负责实现对应的具体的产品。
demo:
抽象工厂接口
package factroy_method;
/**
* @ClassName: ProductFactoryInterface
* @Description: 抽象工厂接口
* @date 2016年5月18日 下午2:03:04
*
* @author leobert.lan
* @version 1.0
*/
public interface ProductFactoryInterface {
public Product createProduct();
}
实际工厂(这里我举例用两个)
package factroy_method;
/**
* @ClassName: AProductFactroy
* @Description: A产品具体工厂
* @date 2016年5月18日 下午2:06:28
*
* @author leobert.lan
* @version 1.0
*/
public class AProductFactory implements ProductFactoryInterface {
@Override
public Product createProduct() {
return new AProduct();
}
}
package factroy_method;
/**
* @ClassName: BProductFactory
* @Description: B产品具体工厂
* @date 2016年5月18日 下午2:13:59
*
* @author leobert.lan
* @version 1.0
*/
public class BProductFactory implements ProductFactoryInterface{
@Override
public Product createProduct() {
return new BProduct();
}
}
“风格类似”的产品的API
package factroy_method;
/**
* @ClassName: Product
* @Description: “产品”的API
* @date 2016年5月18日 下午2:04:36
*
* @author leobert.lan
* @version 1.0
*/
public interface Product {
void use();
}
实际产品类
package factroy_method;
/**
* @ClassName: AProduct
* @Description: 具体产品A
* @date 2016年5月18日 下午2:10:02
*
* @author leobert.lan
* @version 1.0
*/
public class AProduct implements Product{
@Override
public void use() {
System.out.println("use A");
}
}
package factroy_method;
/**
* @ClassName: BProduct
* @Description: 具体产品“B”
* @date 2016年5月18日 下午2:12:36
*
* @author leobert.lan
* @version 1.0
*/
public class BProduct implements Product{
@Override
public void use() {
System.out.println("use B");
}
}
测试一下:
package factroy_method;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月18日 下午2:15:19
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
ProductFactoryInterface productFactoryInterface1 = new AProductFactory();
productFactoryInterface1.createProduct().use();
ProductFactoryInterface productFactoryInterface2 = new BProductFactory();
productFactoryInterface2.createProduct().use();
}
}
输出结果自然是:
use A
use B
我们可以体会到,工厂模式是为了“风格相同”的东西而产生的,我们去使用必然是因为有“风格相同”的东西,每种东西应该是对应了不同的业务场景,可以大胆推测,这些业务也较为类似,潜台词就是:业务类可以进行抽象。
父类只需要拿到抽象工厂接口实例(具体业务子类提供),就可以完成共有的逻辑,不必关心也不应当关心使用的是哪种实例。
有这样一句话:
Factory Method 使一个类的实例化延迟到其子类。
适用性:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
抽象工厂
抽象工厂模式是一种比工厂模式抽象程度更高的模式。在工厂方法模式中,一个具体的工厂类负责创建一个单独的、具体的产品。有多少种产品,就需要多少个对应的工厂类,即使这些产品之间有某些必要联系。
我们可以这样理解:抽象工厂是对工厂方法的进一步抽象,所以他的层面会高一层。通俗但是不是很准确的说就是:工厂模式是干某件事情的,抽象工厂就是干这一类事情的。
上面我们体会到工厂方法模式用来生产“风格类似”的产品,用一个更常见的词:一类产品簇(也有叫产品族的,我习惯用簇这个词),那么抽象工厂就是指导生产多类产品簇的。
这样理解产品簇:碳酸饮料是一个簇,可以有可口可乐、雪碧等等。
而且我们不可否认,同一个产品也有不同属性,例如可口可乐有不同的容量、不同的产地、不同的生产日期(用工厂方法模式也能去做这件事情,上文没有提及,我们可以在工厂接口中定义生产方法的时候约定形参。之所以在这里补充,是有一些文章将这一点作为工厂方法和抽象工厂的不同,这不是很恰当,但抽象工厂确实可以给产品加上每个工厂自己特有的信息)
demo(有些地方偷了点懒)
抽象工厂接口
package abstract_factory;
/**
* @ClassName: AbstractFactory
* @Description: TODO
* @date 2016年5月18日 下午2:47:19
*
* @author leobert.lan
* @version 1.0
*/
public interface AbstractFactory {
public IProductClusterOne createClusterOne();
public IProductClusterTwo createClusterTwo();
}
实际的工厂
package abstract_factory;
/**
* @ClassName: AmericanFactory
* @Description: 美国的具体工厂
* @date 2016年5月18日 下午3:02:32
*
* @author leobert.lan
* @version 1.0
*/
public class AmericanFactory implements AbstractFactory{
@Override
public IProductClusterOne createClusterOne() {
return new AProductOfClusterOne(getClass().getSimpleName());
}
@Override
public IProductClusterTwo createClusterTwo() {
return new AProductOfClusterTwo(getClass().getSimpleName());
}
}
package abstract_factory;
/**
* @ClassName: ChinaFactory
* @Description: TODO
* @date 2016年5月18日 下午3:02:06
*
* @author leobert.lan
* @version 1.0
*/
public class ChinaFactory implements AbstractFactory{
@Override
public IProductClusterOne createClusterOne() {
//同一个产品可以有不同的属性,我直接打上工厂特有的信息,但这不是抽象工厂提出的主要目的,可以这样写而已,在某些情况下确实方便。使用抽象工厂,这里应该生成BProduct而非AProduct
return new AProductOfClusterOne(getClass().getSimpleName());
}
@Override
public IProductClusterTwo createClusterTwo() {
//实际目的就是生产产品簇中的另一个产品
return new BProductOfClusterTwo(getClass().getSimpleName());
}
}
产品簇API
package abstract_factory;
/**
* @ClassName: IProductClusterOne
* @Description: TODO
* @date 2016年5月18日 下午2:59:33
*
* @author leobert.lan
* @version 1.0
*/
public interface IProductClusterOne {
void readInfo();
/**
* @Title: getYieldly
* @Description: 获取生产地信息
* @author: leobert.lan
* @return
*/
String getYieldly();
}
package abstract_factory;
/**
* @ClassName: IProductClusterTwo
* @Description: 第二类产品簇的API,当然,我只是偷懒和第一类写的一样
* @date 2016年5月18日 下午2:59:40
*
* @author leobert.lan
* @version 1.0
*/
public interface IProductClusterTwo {
void readInfo();
String getYieldly();
}
实际产品
package abstract_factory;
/**
* @ClassName: AProductOfClusterOne
* @Description: TODO
* @date 2016年5月18日 下午3:13:41
*
* @author leobert.lan
* @version 1.0
*/
public class AProductOfClusterOne implements IProductClusterOne{
private final String yieldly;
public AProductOfClusterOne(String yieldly) {
this.yieldly = yieldly;
}
@Override
public void readInfo() {
System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
}
@Override
public String getYieldly() {
return yieldly;
}
}
package abstract_factory;
/**
* @ClassName: AProductOfClusterOne
* @Description: TODO
* @date 2016年5月18日 下午3:13:41
*
* @author leobert.lan
* @version 1.0
*/
public class AProductOfClusterTwo implements IProductClusterTwo{
private final String yieldly;
public AProductOfClusterTwo(String yieldly) {
this.yieldly = yieldly;
}
@Override
public void readInfo() {
System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
}
@Override
public String getYieldly() {
return yieldly;
}
}
package abstract_factory;
/**
* @ClassName: BProductOfClusterOne
* @Description: TODO
* @date 2016年5月18日 下午3:15:38
*
* @author leobert.lan
* @version 1.0
*/
public class BProductOfClusterOne implements IProductClusterOne{
private final String yieldly;
public BProductOfClusterOne(String yieldly) {
this.yieldly = yieldly;
}
@Override
public void readInfo() {
System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
}
@Override
public String getYieldly() {
return yieldly;
}
}
package abstract_factory;
/**
* @ClassName: BProductOfClusterOne
* @Description: TODO
* @date 2016年5月18日 下午3:15:38
*
* @author leobert.lan
* @version 1.0
*/
public class BProductOfClusterTwo implements IProductClusterTwo{
private final String yieldly;
public BProductOfClusterTwo(String yieldly) {
this.yieldly = yieldly;
}
@Override
public void readInfo() {
System.out.println("i am"+getClass().getSimpleName()+"made in " + getYieldly());
}
@Override
public String getYieldly() {
return yieldly;
}
}
测试一下
package abstract_factory;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月18日 下午3:27:29
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
AbstractFactory factory1 = new AmericanFactory();
AbstractFactory factory2 = new ChinaFactory();
factory1.createClusterOne().readInfo();
factory2.createClusterOne().readInfo();
factory1.createClusterTwo().readInfo();
factory2.createClusterTwo().readInfo();
}
}
结果:(抱歉没处理好空格。。。)
i amAProductOfClusterOnemade in AmericanFactory
i amAProductOfClusterOnemade in ChinaFactory
i amAProductOfClusterTwomade in AmericanFactory
i amBProductOfClusterTwomade in ChinaFactory
-
意图:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性:
一个系统要独立于它的产品的创建、组合和表示时。
一个系统要由多个产品系列中的一个来配置时。
对比一下两者:
工厂方法 :用来生产同一等级结构中的固定产品。(支持增加任意产品,一个工厂对应一个产品,加产品就加工厂)
抽象工厂 :用来生产不同产品簇的全部产品。(对于增加新的产品,无能为力[这里指在某个具体的工厂中生产一个产品簇中的多个产品];支持增加产品簇)
建造者
对于复杂的创建,我们可以使用这个
可以想象一下,有一个类有很多个属性,嗯,看到构造器真是屎一样的代码,你无法避免这种情况:这么多属性都是final修饰的!好吧,客观的说你是可以使用大量的重载构造器方法以填写默认值,你也可以不使用final修饰在构造后不停的set,但是无法否认你写的时候心里都难受。
对于建造者模式的实现,略有差异,但我更喜欢在预设建造过程的时候返回建造者对象
demo
建造者接口
package builder;
/**
* @ClassName: IHeHeBuilder
* @Description: TODO
* @date 2016年5月18日 下午4:24:55
*
* @author leobert.lan
* @version 1.0
*/
public interface IHeHeBuilder {
IHeHeBuilder setAttrA(String attra);
IHeHeBuilder setAttrB(String attrb);
IHeHeBuilder setAttrC(String attrc);
IHeHeBuilder setAttrD(String attrd);
//....
HeHe build(); //build or create
}
想要建造的类
package builder;
/**
* @ClassName: HeHe
* @Description: TODO
* @date 2016年5月18日 下午4:24:11
*
* @author leobert.lan
* @version 1.0
*/
public class HeHe {
private final String attrA;
private final String attrB;
private final String attrC;
private final String attrD;
public HeHe(String attrA, String attrB, String attrC, String attrD) {
super();
this.attrA = attrA;
this.attrB = attrB;
this.attrC = attrC;
this.attrD = attrD;
}
public void readInfo() {
String s = "a:" + attrA + ",b:" + attrB + ",c" + attrC + ",d:" + attrD;
System.out.println(s);
}
public static class Builder implements IHeHeBuilder {
private String attrA = "defaulta";
private String attrB = "defaultb";
private String attrC = "defaultc";
private String attrD = "defaultd";
public Builder() {
super();
}
// 省略N种情况,强迫症患者最好只写上面那个,也就是一个都不写
public Builder(String attrA, String attrB, String attrC, String attrD) {
super();
this.attrA = attrA;
this.attrB = attrB;
this.attrC = attrC;
this.attrD = attrD;
}
public IHeHeBuilder setAttrA(String attrA) {
this.attrA = attrA;
return this;
}
public IHeHeBuilder setAttrB(String attrB) {
this.attrB = attrB;
return this;
}
public IHeHeBuilder setAttrC(String attrC) {
this.attrC = attrC;
return this;
}
public IHeHeBuilder setAttrD(String attrD) {
this.attrD = attrD;
return this;
}
@Override
public HeHe build() {
return new HeHe(attrA, attrB, attrC, attrD);
}
}
}
测试一下
package builder;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月18日 下午4:36:36
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
IHeHeBuilder builder = new HeHe.Builder();
builder.setAttrA("this is test attr a").setAttrC("hehe").build().readInfo();
}
}
写起来还是挺舒服的
来一段高大上的
意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
原型
原型模式有两种表现形式(本质还是一致的):简单形式,登记形式,上面的图是简单形式的。
值得一提的是,这是一种“复制”的工作方式。复制有浅复制和深复制之分。我们说对象的字段可以分为值类型和引用类型的。浅复制和深复制对值类型的都是拷贝值,是相互独立的,而浅复制的引用类型字段是指向了同一个内存地址,一个变了就都变了,但深复制还是将依赖对象的“内容”进行复制,相互独立。
我们知道java中的Object类中定义了clone方法,Cloneable接口也是一个空的接口。我们抛开这些东西,重新实现一下。
我们主要看一下深浅复制的差别,就不建造多个原型类来展示它的真正意义
demo
package prototype;
/**
* @ClassName: Prototype
* @Description: TODO
* @date 2016年5月19日 上午8:47:38
*
* @author leobert.lan
* @version 1.0
*/
public interface Prototype {
public Object shallowClone();
public Object deepClone();
}
原型类(真正使用的时候会建立多个原型类来当作“模板”,参数基本都是固定的,制式的风格)
package prototype;
/**
* @ClassName: APrototype
* @Description: TODO
* @date 2016年5月19日 上午10:45:19
*
* @author leobert.lan
* @version 1.0
*/
public class APrototype implements Prototype {
private String s;
// 一个default 的引用
private Reliant reliant = new Reliant(0);
public APrototype(String s) {
this.s = s;
}
public Reliant getReliant() {
return reliant;
}
public void setReliant(Reliant reliant) {
this.reliant = reliant;
}
public void readInfo() {
System.out.println(s+",reliant info:"+reliant.getI());
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
@Override
public Object shallowClone() {
APrototype ret = new APrototype(s);
ret.setReliant(reliant);
return ret;
}
@Override
public Object deepClone() {
APrototype ret = new APrototype(s);
ret.setReliant(new Reliant(reliant.getI()));
return ret;
}
}
测试一下深浅复制的区别
package prototype;
/**
* @ClassName: Test
* @Description: TODO
* @date 2016年5月19日 上午10:52:24
*
* @author leobert.lan
* @version 1.0
*/
public class Test {
public static void main(String[] args) {
Prototype prototype = new APrototype("1");
APrototype deepCloneTest1 = (APrototype) prototype.deepClone();
APrototype deepCloneTest2 = (APrototype) prototype.deepClone();
deepCloneTest1.setS("deepCloneTest1");
deepCloneTest2.setS("deepCloneTest2");
deepCloneTest1.getReliant().setI(1);
deepCloneTest2.getReliant().setI(2);
deepCloneTest1.readInfo();
deepCloneTest2.readInfo();
System.out.println("========================");
APrototype shallowCloneTest1 = (APrototype) prototype.shallowClone();
APrototype shallowCloneTest2 = (APrototype) prototype.shallowClone();
shallowCloneTest1.setS("shallowCloneTest1");
shallowCloneTest2.setS("shallowCloneTest2");
shallowCloneTest1.getReliant().setI(1);
shallowCloneTest2.getReliant().setI(2);
shallowCloneTest1.readInfo();
shallowCloneTest2.readInfo();
}
}
我修改了一下分割,忘了这是markdown
deepCloneTest1,reliant info:1
deepCloneTest2,reliant info:2
!************
shallowCloneTest1,reliant info:2
shallowCloneTest2,reliant info:2
意图:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性:
当要实例化的类是在运行时刻指定时,例如,通过动态装载;
或者为了避免创建一个与产品类层次平行的工厂类层次时;
或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
另外,深复制往往采用序列化、反序列化手段处理,而且牵涉到一个深度问题,也会有相互依赖的问题,需要注意
单例
单例模式还是用的比较多的,没有必要给demo了
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
后记
本想一次性全部写完,但这是奢望啊,这两天已经算比较闲散的了,慢慢来吧,争取分三次写完。demo源码等写完了再上传到下载区吧。