根据 设计模式 整理。
设计模式的一般定义如下: 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。
创建型模式主要用于描述如何创建对象。
工厂模式
当一个类需要创建各种不同类型的实例时,可以使用工厂模式分为工厂和产品两个部分,工厂类负责生成产品实例,产品类负责业务方法。
简单工厂模式
简单工厂模式包括三个角色:
- Factory(工厂角色)
- Product(抽象产品角色),是所有具体产品的父类
- ConcreteProduct(具体产品角色)
abstract class Product {
//所有产品类的公共业务方法
public void methodSame() {
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}
class ConcreteProduct extends Product {
//实现业务方法
public void methodDiff() {
//业务方法的实现
}
}
class Factory {
//静态工厂方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化设置product
}
else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}
这样工厂类就能根据不同参数生产不同的产品了
Product product;
product = Factory.getProduct("A"); //通过工厂类创建产品对象
product.methodSame();
product.methodDiff();
当产品类型较多时,工厂类逻辑会变得过于复杂,不利于扩展。
工厂方法模式
工厂方法模式是通过一个抽象工厂类和具体工厂类来实现对具体产品类的生产,解决简单工厂模式中工厂类臃肿复杂的问题。其包括四个角色:
- Factory(抽象工厂)抽象类,声明一个工厂方法,返回一个产品
- ConcreteFactory(具体工厂)实现抽象工厂的方法,返回一个具体产品
- Product(抽象产品),是所有具体产品的父类
- ConcreteProduct(具体产品)
interface Factory {
public Product factoryMethod();
}
class ConcreteFactory implements Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
abstract class Product {
//所有产品类的公共业务方法
public void methodSame() {
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}
class ConcreteProduct extends Product {
//实现业务方法
public void methodDiff() {
//业务方法的实现
}
}
使用
Factory factory;
factory = new ConcreteFactory(); //可通过配置文件实现
Product product;
product = factory.factoryMethod();
这种方式虽然减轻了单个工厂类的压力,但是当增加新的产品时,类的个数会成对增加,仍会导致系统越来越庞大。
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,每一个产品族都位于某个产品等级结构。其包括四个角色:
- AbstractFactory(抽象工厂)它声明了一组用于创建一族产品的方法,每一个方法对应一种产品
- ConcreteFactory(具体工厂)它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
- Product(抽象产品)是所有具体产品的父类
- ConcreteProduct(具体产品)
abstract class AbstractFactory {
public abstract AbstractProductA createProductA(); //工厂方法一
public abstract AbstractProductB createProductB(); //工厂方法二
……
}
class ConcreteFactory1 extends AbstractFactory {
//工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
//工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}
……
}
抽象产品和产品类和工厂方法模式相同
使用
Factory factory;
factory = new ConcreteFactory1(); //可通过配置文件实现
Product productA;
Product productB;
productA = factory.createProductA();
productB = factory.createProductB();
单例模式
需要确保系统中某个类只有一个实例,此时就可以使用单例模式
饿汉式单例
把构造方法设为私有,持有自身的一个静态实例,然后使用一个静态方法返回此实例。实例化的过程发生在类加载的时候。
class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() { }
public static EagerSingleton getInstance() {
return instance;
}
}
懒汉式单例
懒汉式和饿汉式的区别是把类实例化的过程写在了静态方法 getInstance 中,也就是当第一次调用 getInstance 方法时才实例化对象。为了避免多个线程同时调用产生冲突,需要加上线程锁。
class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
synchronized public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
对比
饿汉式不论是否使用都会在加载类的时候生成实例,占用内存,调用时响应速度快,线程安全;
懒汉式只有在第一次调用时才会生成实例,但线程不安全,需要使用线程锁,会影响调用性能。
Initialization Demand Holder (IoDH)
使用 IoDH 方式可以同时解决饿汉式和懒汉式的缺点:在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用。
class Singleton {
private Singleton() {
}
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
public static void main(String args[]) {
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}
原型模式
通过一个原型对象克隆出多个一模一样的对象,该模式称之为原型模式。
在原型模式结构图中包含如下几个角色:
Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
浅克隆和深克隆:
浅克隆中,复制的对象和被复制的对象本身虽然在内存中处于不同的地址,但是其中的引用类型的成员变量仍然指向同一个内存地址,也就是说对于成员变量并未复制。而深克隆中支持引用类型的成员变量的复制。
浅克隆实现
一种通用的实现方式:
class ConcretePrototype implements Prototype
{
private String attr; //成员属性
public void setAttr(String attr)
{
this.attr = attr;
}
public String getAttr()
{
return this.attr;
}
public Prototype clone() //克隆方法
{
Prototype prototype = new ConcretePrototype(); //创建新对象
prototype.setAttr(this.attr);
return prototype;
}
}
使用:
Prototype obj1 = new ConcretePrototype();
obj1.setAttr("Sunny");
Prototype obj2 = obj1.clone();
java 中 Object 类提供了一个 project 的 clone() 方法,可以直接用此实现原型。需要注意的是必须要实现 Cloneable 接口,否则会抛出一个 CloneNotSupportedException 异常。实现方式:
class ConcretePrototype implements Cloneable
{
……
public Prototype clone()
{
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception) {
System.err.println("Not support cloneable");
}
return (Prototype )object;
}
……
}
使用
Prototype obj1 = new ConcretePrototype();
Prototype obj2 = obj1.clone();
深克隆实现
java 中实现深克隆需要通过序列化的方式实现。
import java.io.*;
//工作周报类
class WeeklyLog implements Serializable
{
private Attachment attachment;
private String name;
private String date;
private String content;
//使用序列化技术实现深克隆
public WeeklyLog deepClone() throws IOException, ClassNotFoundException, OptionalDataException
{
//将对象写入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (WeeklyLog)ois.readObject();
}
}
原型管理器
原型模式通常在需要模板对象时使用,如果同时有多个原型对象模板,可以使用一个工厂类来专门负责生产克隆对象。
import java.util.*;
//抽象公文接口,也可定义为抽象类,提供clone()方法的实现,将业务方法声明为抽象方法
interface OfficialDocument extends Cloneable
{
public OfficialDocument clone();
public void display();
}
//可行性分析报告(Feasibility Analysis Report)类
class FAR implements OfficialDocument
{
public OfficialDocument clone()
{
OfficialDocument far = null;
try
{
far = (OfficialDocument)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("不支持复制!");
}
return far;
}
public void display()
{
System.out.println("《可行性分析报告》");
}
}
//软件需求规格说明书(Software Requirements Specification)类
class SRS implements OfficialDocument
{
public OfficialDocument clone()
{
OfficialDocument srs = null;
try
{
srs = (OfficialDocument)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("不支持复制!");
}
return srs;
}
public void display()
{
System.out.println("《软件需求规格说明书》");
}
}
//原型管理器(使用饿汉式单例实现)
class PrototypeManager
{
//定义一个Hashtable,用于存储原型对象
private Hashtable ht=new Hashtable();
private static PrototypeManager pm = new PrototypeManager();
//为Hashtable增加公文对象
private PrototypeManager()
{
ht.put("far",new FAR());
ht.put("srs",new SRS());
}
//增加新的公文对象
public void addOfficialDocument(String key,OfficialDocument doc)
{
ht.put(key,doc);
}
//通过浅克隆获取新的公文对象
public OfficialDocument getOfficialDocument(String key)
{
return ((OfficialDocument)ht.get(key)).clone();
}
public static PrototypeManager getPrototypeManager()
{
return pm;
}
}
建造者模式
当一个产品由多个复杂部件组成时,可以使用建造者模式。建造者模式将多个复杂的部件的创建过程进行分离,从而一步步创建一个复杂的对象。
包括四个角色:
Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。
ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
实现方式:
class Product {
private String partA; //定义部件,部件可以是任意类型,包括值类型和引用类型
private String partB;
private String partC;
//partA的Getter方法和Setter方法省略
//partB的Getter方法和Setter方法省略
//partC的Getter方法和Setter方法省略
}
abstract class Builder {
//创建产品对象
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder=builder;
}
public void setBuilder(Builder builder) {
this.builder=builer;
}
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
使用:
Builder builder = new ConcreteBuilder(); //可通过配置文件实现
Director director = new Director(builder);
Product product = director.construct();
无设计者的建造者模式
我们在 android 中的建造者模式随处可见,如 AlertDialog,Okhttp,ImageLoader 等。这些类的使用时是没有设计者的,通常需要我们直接调用 Builder 类的 builder 方法来创建对象,并且允许客户端根据自己的需要直接配置产品部件。
AndroidStudio 中可以使用插件 “InnerBuilder” 来在产品类中自动生成 Builder 构建类,你只需写好其中的成员变量即可。
代码示例:
public class CrashUtils {
private ShardPreCollector mShardPreCollector;
private SysSettingCollector mSettingCollector;
private String folderPath;
private CrashUtils(Builder builder) {
mShardPreCollector = builder.mShardPreCollector;
mSettingCollector = builder.mSettingCollector;
folderPath = builder.folderPath;
}
public void getCrash(Thread thread,Throwable throwable){
LogUtils.e(throwable);
LogUtils.e(thread);
StringBuilder resulet=new StringBuilder();
if (mSettingCollector!=null){
resulet.append(mSettingCollector.collect());
}
if (mShardPreCollector!=null){
resulet.append(mShardPreCollector.collect());
}
LogUtils.i(resulet);
}
public static final class Builder {
private ShardPreCollector mShardPreCollector;
private SysSettingCollector mSettingCollector;
private String folderPath;
public Builder() {
}
public Builder mShardPreCollector(ShardPreCollector val) {
mShardPreCollector = val;
return this;
}
public Builder mSettingCollector(SysSettingCollector val) {
mSettingCollector = val;
return this;
}
public Builder folderPath(String val) {
folderPath = val;
return this;
}
public CrashUtils build() {
return new CrashUtils(this);
}
}
}
使用时:
CrashUtils crashUtils=new CrashUtils.Builder()
.folderPath("zzh")
.mSettingCollector(new SysSettingCollector(this))
.mShardPreCollector(new ShardPreCollector(this,new String[]{"pre"}))
.build();