前言:近两周,利用早餐半小时的时间读完了《大话设计模式》(作者:程杰),的确是本好书,用比较幽默的方式结合故事描述了二十来种设计模式。众所周知,纯理论性的教科书是催眠必备之良药,然鹅这本主要讲理论,讲概念的技术书你可以把他当故事书看,每天看一篇,每天进步一点点!这里推荐给大家。
我知道,设计模式这个东西要运用到实际开发中去绝对不是看看书,理解了概念就ok的,但这也是必要条件。写下这篇文章的目的一是推荐这本好书,另外也是帮自己巩固、加强一下理解。
设计模式分类:
1.创建型模式
2.结构型模式
3.行为型模式
本篇主要总结下创建型模型
- 单例模式
- 原型模式
- 建造者模式
- 厂妹联盟(简单工场、工场方法、抽象工场)
单例模式
1.定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.优缺点:
由于单例在内存仅一个实例,减少了内存的开支。
驻留在内存,又减少了频繁创建和销毁的性能开销。
设置全局的访问点,共享资源,避免对同一资源的多重占用。
扩展困难、持有context或其他对象,容易引起内存泄漏。
3.总结:例如安卓开发中的UserInfoManage,故名思议,全局的管理用户信息的类,使用单例模式。
需要注意线程安全,处理的方式有两种 双重锁定和静态初始化。其中静态初始化是类加载就初始化,又名饿汉模式,
用的时候判空创建的叫懒汉模式。
public staic Singleton GetInstance(){ //douuble check lock DCL
if(mInstance=null){
synchronized(Singleton.class)
if(mInstance=null){
mInstance=new Singleton();
}
}
return mInstance;
}
原型模式
1.定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
2.优缺点:实现是在内存中二进制流的拷贝
,比直接new性能会好很多(在循环体中产生大量对象时更明显),由于直接在内存里拷贝,不会走构造函数
3.总结:clone分为深拷贝和浅拷贝,如果对象A中包含有对象B,仅仅对A克隆的话,克隆出的A对象里的B和A里的B是同一引用,也就是对应同一内存地址,这就是浅拷贝。如果在A的克隆实现里对B也进行了克隆,那就是深拷贝。
public class Book implements Cloneable {
private String bookName;
private List<String> chapters;
//浅克隆实现
protected Book clone(){
Book cloneBook=(Book)super.clone;
cloneBook.bookname=this.bookName;
cloneBook.chapters=this.chapters;
}
//深克隆实现
protected Book clone(){
Book cloneBook=(Book)super.clone;
cloneBook.bookname=this.bookName;
cloneBook.chapters=(List<String>)this.chapters.clone();
}
}
建造者模式
1.定义:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2.优缺点:
使用建造者模式客户端不必知道产品内部的细节。
稳定的输入功能形态大体相同,细节自定义的对象
产生多余的Builder和Director对象,消耗内存。
3.总结
开发时经常将Builder作为产品的内部类去简单使用,这样存在的问题是当需求改变,并且涉及到Builder未封装的属性时就必须去修改产品内里的budlier,甚至create。例如信e家的对话框,之前统一风格为标题,取消,确定三个按钮,后面有个需求是增加一个带半悬浮顶部ico的对话框,由于设计Builder的时候没有考虑布局变化,没有封装setLaout方法,所以处理方式是在create()方法下面新增了一个createWithTopIco的创建方法,大部分代码一样,仅仅是替换了布局id,没有起到复用的目的,也违背了开放闭合原则。
简单工厂
1.定义:不属于23种GOF设计模式,一个静态工厂类,根据传入的参数通过switch直接返回具体产品
2.优缺点:
简单实用,逻辑简单
违背了开放闭合原则,每次增加新产品都需要修改工厂类
3.疑惑:配合反射,完全可以做到新增产品,不去修改工厂类同样返回具体产品,那么这种情况下跟工场方法比较孰优孰劣,有何异同??
个人看法:由于工场方法中具体产品有具体工厂,工厂内有create()方法来生产这个产品,那么当需要对生产出来的产品进行一些初始化的时候完全可以在工场方法中create()里进行扩展,但是同样的 简单工场+反射可能就无能为力,因为所有的产品都在这个工场,如果强行对具体产品加上初始化,那么产品一多,这个工厂类必然又臃肿不堪。
工厂方法
1.定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
2.优缺点:
工厂方法封装了对象创建的细节,将对象创建代码和其他部分脱离,减少相干性
有利于同类对象创建的统一管理和控制
不必关心实现细节,只关心工场提供的方法
缺点:当产品演变成产品系列时,工场方法无能无力。(文末举例,对比抽象工厂)
抽象工厂
1.定义:提供一个创建一系列相关或相互依赖
对象的接口,而无需指定它们具体的类。
2.总结:抽象工厂创建的是一系列产品,并且产品相关或相互依赖
(重点)
3.优点同工厂方法,分离接口与实现,切换一下new 出来的具体工厂,所有的产品自动切换。
缺点:增加产品线时适用,但增加产品类型时修改较大。
对比工厂方法和抽象工厂:
例:
当产品是 奥迪车,奔驰车,五菱车 只是增加车的品牌的话工场方法是适用的
但是如果产品不是车,而是一个系列,有一条产品线的话:
产品类别:轮胎,制动系统。车标。
产品线:奥迪系列,奔驰系列,五菱系列。
奥迪轮胎,奥体制动系统,奥迪车标
奔驰车轮胎,奔驰车制动系统,奔驰车标
五菱车轮胎,五菱车车制动系统,五菱车车标
这时候就该考虑抽象工厂了:
抽象类工厂:
轮胎工厂、制动系统工厂、车标工厂
具体工厂类:
奥迪汽车配件工厂(造奥迪轮胎,造奥迪制动系统、造奥迪车标)
奔驰汽车配件工厂(造奔驰轮胎,造奔驰制动系统、造奔驰车标)
五菱汽车配件工厂(造五菱轮胎,造五菱制动系统、造五菱车标)
抽象工厂完美实现。
此时,有两种情况:
1.增加产品线,比如增加一个大众系列
对应我们新增一个 大众系列配件工厂(造大众轮胎,造大众制动系统、造大众车标) 就ok了
2.增加产品类别,如不光考虑轮胎,制动系统,车标, 还要考虑引擎
这时候如果使用的是抽象工厂,扩展就不太方便了,需要修改所有的工厂类。
结论:
工厂方法:适用于产品结构单一的情况(奥迪车就是奥迪车,不分什么奥迪轮胎,奥迪车标等),一个工厂生产一种具体产品, 扩展产品时增加对应工厂就行,符合开放封闭原则。
抽象工厂:适用于多个产品,每个产品拥有不同产品结构(如轮胎这个产品,有奥迪的,有大众的,并不通用),并且结构相同
的不同产品
之间有依赖或关联(如奥迪轮胎必须配奥迪车标 ~ 不然交警说你改造私家车~~),扩展产品结构的时候新增一个 新结构 的工厂就行(这里的结构
可以理解为 品牌)