一、简介
创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。
创建型模式(Creational Pattern)
-
单例模式 Singleton Pattern: 单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
使用频率:★★★★☆ -
简单工厂模式 Simple Factory Pattern: 又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
使用频率:★★★☆☆ -
工厂方法模式 Factory Method Pattern: 是一种常用的类创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。
使用频率:★★★★★ -
抽象工厂模式 Abstract Factory Pattern: 是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
使用频率:★★★★★ -
原型模式 Prototype Pattern: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
使用频率:★★★☆☆ -
建造者模式 Builder Pattern: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用频率:★★☆☆☆
二、单例模式
2.1、饿汉式
/**
* 单例模式(饿汉式)
* 线程安全,没有加锁,执行效率会提高。
* 类加载时就初始化,浪费内存,容易产生垃圾对象
* <p>
* 单例类使用了final修饰,防止被继承
* 这种写法比较简单,就是在类装载的时候就完成实例化。instance作为类变量,在类初始化过程中,会被收集进<clinit>()方法中,该方法会保障同步,从而避免了线程同步问题。
* 在类装载的时候就完成实例化,若从未使用过这个实例,则会造成内存的浪费。
*/
public final class SingleObject1 {
//创建 SingleObject1 的一个对象
private static SingleObject1 instance = new SingleObject1();
//私有化构造函数,这样该类就不会被实例化
private SingleObject1() {}
//获取唯一可用的对象
public static SingleObject1 getInstance() {
return instance;
}
public void say() {
System.out.println("Hello");
}
}
……
//调用
SingleObject1.getInstance().say();//Hello
2.2、懒汉式,线程不安全
/**
* 单例模式(懒汉式,线程不安全)
* 线程不安全,没有加锁。
* 此方式在确定是单线程的情况下,可以保证创建的对象唯一性
*/
public final class SingleObject2 {
//创建 SingleObject1 的一个对象
private static SingleObject2 instance;
//私有化构造函数,这样该类就不会被实例化
private SingleObject2() {}
//获取唯一可用的对象
public static SingleObject2 getInstance() {
if (instance == null) {
instance = new SingleObject2();
}
return instance;
}
public void say() {
System.out.println("Hello");
}
}
……
//调用
SingleObject2.getInstance().say();//Hello
2.3、懒汉式,线程安全
/**
* 单例模式(懒汉式,线程安全)
* 线程安全,加锁,执行效率低
*/
public final class SingleObject3 {
//创建 SingleObject1 的一个对象
private static SingleObject3 instance;
//私有化构造函数,这样该类就不会被实例化
private SingleObject3() {}
//获取唯一可用的对象
public synchronized static SingleObject3 getInstance() {
if (instance == null) {
instance = new SingleObject3();
}
return instance;
}
public void say() {
System.out.println("Hello");
}
}
……
//调用
SingleObject3.getInstance().say();//Hello
2.4、双检锁/双重校验锁
/**
* 单例模式(双检锁/双重校验锁)
* 线程安全
* 种方式采用双锁机制,安全且在多线程情况下能保持高性能
*/
public final class SingleObject4 {
//创建 SingleObject1 的一个对象
private volatile static SingleObject4 instance;
//私有化构造函数,这样该类就不会被实例化
private SingleObject4() {}
//获取唯一可用的对象
public static SingleObject4 getInstance() {
if (instance == null) {
synchronized (SingleObject4.class) {
if (instance == null) {
instance = new SingleObject4();
}
}
}
return instance;
}
public void say() {
System.out.println("Hello");
}
}
……
//调用
SingleObject4.getInstance().say();//Hello
2.5、静态内部类方式
/**
* 单例模式(静态内部类实现模式)
* 线程安全,调用效率高,可以延时加载
* 这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。
* 这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
* 这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,
* <p>
* 静态内部类方式是最好也是最常用的几种实现单例模式的方式之一
*/
public final class SingleObject5 {
private static class SingleObject5Holder {
private static final SingleObject5 instance = new SingleObject5();
}
//私有化构造函数,这样该类就不会被实例化
private SingleObject5() {}
//获取唯一可用的对象
public static SingleObject5 getInstance() {
return SingleObject5Holder.instance;
}
public void say() {
System.out.println("Hello");
}
}
……
//调用单例
SingleObject5.getInstance().say();//Hello
2.6、枚举方式
/**
* 单例模式(枚举方式,Android不推荐)
* 线程安全,调用效率高,不能延时加载,可以防止反射和反序列化调用
* 这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
* 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,
* 防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
* 不能通过 reflection attack 来调用私有构造方法。
*
* 枚举实现单例,不推荐在Android平台使用,因为内存消耗会其他方式多一些,Android官方也不推荐枚举,
* Android平台推荐双重校验或者静态内部类单例,现在的Android开发环境jdk一般都大于1.5了。所以volatile的问题不必担心。
* Java平台开发的Effective Java一书中推荐使用枚举实现单例,可以保证效率,而且还能解决反序列化创建新对象的问题。
*/
public enum SingleObject6 {
INSTANCE;
public void say() {
System.out.println("Hello");
}
}
……
//调用单例
SingleObject6.INSTANCE.say();//Hello
三、简单工厂模式
3.1、定义接口
定义人接口,吃的动作方法.
public interface Human {
//吃动作
void eat();
}
3.2、定义具体实现
定义中国人.
public class Chinese implements Human {
@Override
public void eat() {
System.out.println("中国人吃饭");
}
}
定义美国人.
public class American implements Human {
@Override
public void eat() {
System.out.println("美国人吃饭");
}
}
3.3、定义工厂类
方式一: 通过type创建工厂类
public class HumanFactory {
public static String CHINESE_HUMAN = "CHINESE_HUMAN";
public static String AMERICAN_HUMAN = "AMERICAN_HUMAN";
/**
* 创建人类工厂方法
* @param type {@link #CHINESE_HUMAN}、{@link #AMERICAN_HUMAN}
* @return 人类
*/
public Human createHuman(@NotNull String type) {
if (type.equals(CHINESE_HUMAN)) {
return new Chinese();
} else if (type.equals(AMERICAN_HUMAN)) {
return new American();
}
return null;
}
}
方式二: 通过java反射方式创建工厂类
public class HumanFactory2 {
/**
* 创建人类工厂方法
* @param c 中国人、美国人
* @param <T>
* @return 人类
*/
public <T extends Human> T createHuman(Class<T> c) {
T t=null;
try {
t = (T) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
System.out.println("不支持抽象类或接口");
e.printStackTrace();
} catch (IllegalAccessException e) {
System.out.println("没有足够权限,即不能访问私有对象");
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("类不存在");
e.printStackTrace();
}
return t;
}
}
3.5、使用简单工厂模式
public static void main(String[] args) {
//----------------------方式一------------------------------
//创建工厂类
HumanFactory humanFactory=new HumanFactory();
//创建美国人
Human human1 = humanFactory.createHuman(HumanFactory.AMERICAN_HUMAN);
human1.eat();//美国人吃饭
//创建中国人
Human human2 = humanFactory.createHuman(HumanFactory.CHINESE_HUMAN);
human2.eat();//中国人吃饭
//----------------------方式二------------------------------
HumanFactory2 humanFactory2 = new HumanFactory2();
Human human3 = humanFactory2.createHuman(American.class);
human3.eat();//美国人吃饭
Human human4 = humanFactory2.createHuman(Chinese.class);
human4.eat();//中国人吃饭
}
四、工厂方法模式
4.1、定义接口
定义人接口,吃的动作方法.
public interface Human {
//吃动作
void eat();
}
定义工厂类接口
//工厂接口类
public interface Factory {
//创建人类
Human createHuman();
}
4.2、定义具体实现
定义中国人.
public class Chinese implements Human {
@Override
public void eat() {
System.out.println("中国人吃饭");
}
}
定义美国人.
public class American implements Human {
@Override
public void eat() {
System.out.println("美国人吃饭");
}
}
定义中国人工厂类
//中国人工厂类
public class ChineseFactory implements Factory {
@Override
public Human createHuman() {
return new Chinese();
}
}
定义美国人工厂类
//美国人工厂类
public class AmericanFactory implements Factory{
@Override
public Human createHuman() {
return new American();
}
}
4.3、使用工厂方法模式
public static void main(String[] args) {
Factory factory1 = new AmericanFactory();
Human human1 = factory1.createHuman();
human1.eat();//美国人吃饭
Factory factory2 = new ChineseFactory();
Human human2 = factory2.createHuman();
human2.eat();//中国人吃饭
}
五、抽象工厂模式
5.1、定义接口
定义人接口,吃的动作方法.
public interface Human {
//吃动作
void eat();
}
定义水果接口
//水果接口
public interface Fruits {
//水果名称
String name();
}
定义工厂接口,每个工厂都有制造人和苹果方法
//工厂接口
public interface Factory {
//创建人类
Human createHuman();
//创建水果
Fruits createFruits();
}
5.2、定义具体实现
定义中国人
public class Chinese implements Human {
@Override
public void eat() {
System.out.println("中国人吃饭");
}
}
定义美国人
public class American implements Human {
@Override
public void eat() {
System.out.println("美国人吃饭");
}
}
定义苹果
//苹果实体
public class Apple implements Fruits{
@Override
public String name() {
System.out.println("苹果");
return "苹果";
}
}
定义葡萄
//葡萄实体
public class Grape implements Fruits {
@Override
public String name() {
System.out.println("葡萄");
return "葡萄";
}
}
定义制造中国人和苹果的工厂
//制造工厂A 制造中国人和苹果
public class CreateFactoryA implements Factory{
@Override
public Human createHuman() {
return new Chinese();
}
@Override
public Fruits createFruits() {
return new Apple();
}
}
定义制造美国人和葡萄的工厂
//制造工厂B 制造美国人和葡萄
public class CreateFactoryB implements Factory{
@Override
public Human createHuman() {
return new American();
}
@Override
public Fruits createFruits() {
return new Grape();
}
}
5.3、使用抽象工厂模式
public static void main(String[] args) {
Factory factory1=new CreateFactoryA();
Human human = factory1.createHuman();
Fruits fruits = factory1.createFruits();
human.eat();//中国人吃饭
fruits.name();//苹果
Factory factory2=new CreateFactoryB();
Human human2 = factory2.createHuman();
Fruits fruits2 = factory2.createFruits();
human2.eat();//美国人吃饭
fruits2.name();//葡萄
}
六、原型模式
原型模式是一个创建型的模式。原型二字表明了改模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。
6.1、浅拷贝实现原型模式
新建Book类实现Cloneable接口,复写clone方法
//书原型(浅拷贝)
public class Book implements Cloneable {
//书名
private String title;
//书页数
private ArrayList<String> page = new ArrayList<String>();
public Book() {
//一个实现了Cloneable并重写了clone方法的类,通过clone得到对象构造函数是不会被执行
System.out.println("构造函数被执行了");
}
@Override
protected Book clone() {
try {
return (Book) super.clone();
} catch (CloneNotSupportedException e) {
//异常处理
e.printStackTrace();
return null;
}
}
public String getTitle() {return title;}
public void setTitle(String title) {this.title = title;}
public List<String> getPage() {return page;}
public void addPage(String page) {this.page.add(page);}
@Override
public String toString() {
return "Book{" +"title='" + title + '\'' +", page=" + page +'}';
}
}
测试原型模式
//浅拷贝测试
Book book = new Book();
book.setTitle("Java");
book.addPage("Java第1章");
System.out.println(book.toString());//Book{title='Java', page=[Java第1章]}
Book cloneBook = book.clone();
cloneBook.setTitle("Android");
cloneBook.addPage("Android第1章");
System.out.println(cloneBook.toString());//Book{title='Android', page=[Java第1章, Android第1章]}
// 再次打印原始书本发现原书信息被修改(因为Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、集合、容器对象、引用对象等都不会拷贝,需要使用深拷贝解决)
System.out.println(book.toString());//Book{title='Java', page=[Java第1章, Android第1章]}
6.2、深拷贝实现原型模式
新建Book类实现Cloneable接口,复写clone方法,因为Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、集合、容器对象、引用对象等都不会拷贝,需要将page进行深度拷贝.
//书原型(深拷贝)
public class Book2 implements Cloneable {
//书名
private String title;
//书页数
private ArrayList<String> page = new ArrayList<String>();
@Override
protected Book2 clone() {
try {
Book2 book2 = (Book2) super.clone();
book2.page= (ArrayList<String>) this.page.clone();
return book2;
} catch (CloneNotSupportedException e) {
//异常处理
e.printStackTrace();
return null;
}
}
public String getTitle() {return title;}
public void setTitle(String title) {this.title = title;}
public List<String> getPage() {return page;}
public void addPage(String page) {this.page.add(page);}
@Override
public String toString() {
return "Book{" +"title='" + title + '\'' +", page=" + page +'}';
}
}
测试原型模式
//深拷贝测试
Book2 book2 = new Book2();
book2.setTitle("Java");
book2.addPage("Java第1章");
System.out.println(book2.toString());//Book{title='Java', page=[Java第1章]}
Book2 cloneBook2 = book2.clone();
cloneBook2.setTitle("Android");
cloneBook2.addPage("Android第1章");
System.out.println(cloneBook2.toString());//Book{title='Android', page=[Java第1章, Android第1章]}
// 再次打印原始书本发现原书没有被改变
System.out.println(book2.toString());//Book{title='Java', page=[Java第1章]}
6.3、使用原型模式注意事项
-
构造函数不会被执行
通过clone复制得到对象,该对象构造函数是不会被执行的。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不 会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。在单例模式中,只要将构造方法的访问权限设置为 private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的。
-
浅拷贝和深拷贝
clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。在开发中,为减少错误,建议在使用原型模式时尽量使用深拷贝,避免操作副本时影响原始对象的问题。如果是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
-
clone与final
要使用clone方法,类的成员变量上不要增加final关键字,因为final类型是不允许重赋值的。
七、建造者模式
建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
7.1、建造者模式的定义
定义产品类
//具体产品类
public class Product {
private String name;
private String desc;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getDesc() {return desc;}
public void setDesc(String desc) {this.desc = desc;}
@Override
public String toString() {
return "Product{" +"name='" + name + '\'' +", desc='" + desc + '\'' +'}';
}
}
定义抽象建造者
//定义抽象建造者
public abstract class Builder {
//设置产品名称
abstract void setName(String name);
//设置产品描述
abstract void setDesc(String desc);
//构建产品
abstract Product builderProduct();
}
定义具体建造者
//定义具体建造者
public class ConcreteProduct extends Builder {
private final Product product;
public ConcreteProduct() { product = new Product();}
@Override
void setName(String name) {
product.setName(name);
}
@Override
void setDesc(String desc) {
product.setDesc(desc);
}
@Override
Product builderProduct() {
return product;
}
}
定义指挥者类
//定义指挥者类
public class Director {
private final ConcreteProduct concreteProduct;
public Director() {
concreteProduct = new ConcreteProduct();
}
public Product getProduct(String name,String desc){
concreteProduct.setName(name);
concreteProduct.setDesc(desc);
return concreteProduct.builderProduct();
}
}
使用建造者模式
public static void main(String[] args) {
Director director = new Director();
Product product = director.getProduct("产品A", "A 的描述");
System.out.println(product);//Product{name='产品A', desc='A 的描述'}
}
7.2、Android中建造者实现
Android中的AlertDialog.Builder就是使用了Builder模式来构建AlertDialog的。
Android AlertDialog简单构建与使用
AlertDialog.Builder builder = new AlertDialog.Builder(activity);//创建一个Builder对象
builder.setIcon(R.drawable.icon);
builder.setTitle("标题");
builder.setMessage("内容");
builder.setPositiveButton("确定", null);
AlertDialog alertDialog = builder.create();//创建AlertDialog对象
alertDialog.show();//显示AlertDialog
通过Builder对象来构建Icon、Title、Message、按钮监听等,将AlertDialog的构建过程和细节隐藏了起来。
AlertDialog部分源码:
public class AlertDialog extends AppCompatDialog implements DialogInterface {
final AlertController mAlert;
protected AlertDialog(@NonNull Context context) {
this(context, 0);
}
……
public static class Builder {
private final AlertController.AlertParams P;//存放构建时设置的参数
……
public Builder(@NonNull Context context) {
this(context, resolveDialogTheme(context, 0));
}
……
public Builder(@NonNull Context context) {//构建Builder
this(context, resolveDialogTheme(context, 0));
}
……
public Builder setTitle(@Nullable CharSequence title) {//设置标题
P.mTitle = title;
return this;
}
……
public Builder setMessage(@Nullable CharSequence message) {//设置内容
P.mMessage = message;
return this;
}
……
public Builder setIcon(@DrawableRes int iconId) {//设置icon
P.mIconId = iconId;
return this;
}
……
public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {//设置按钮和监听
P.mPositiveButtonText = text;
P.mPositiveButtonListener = listener;
return this;
}
……
public AlertDialog create() {//构建AlertDialog
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
P.apply(dialog.mAlert);//将构建时参数通过AlertController运用到AlertDialog上
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {dialog.setCanceledOnTouchOutside(true);}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {dialog.setOnKeyListener(P.mOnKeyListener);}
return dialog;
}
……
}
}
AlertController部分源码:
class AlertController {
public static class AlertParams {
……
public void apply(AlertController dialog) {//将构建时参数通过AlertController运用到AlertDialog上
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
if (mMessage != null) {
dialog.setMessage(mMessage);
}
if (mPositiveButtonText != null || mPositiveButtonIcon != null) {
dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
mPositiveButtonListener, null, mPositiveButtonIcon);
}
……
Android中 AlertDialog
使用是通过AlertDialog.Builder
设置各种属性后(如:setTitle()
),这些属性信息会保存在P(AlertController.AlertParams
)变量中,调用create()
即可返回一个AlertDialog
对象,create()
方法中P.apply(dialog.mAlert)
是将构建时设置的参数通过AlertController
运用到AlertDialog
上,最后调用AlertDialog
中的show()
显示对话框。AlertDialog
通过builder
模式隐藏了这种复杂的构建过程,只需几行简单的代码就把AlertDialog
给展示出来了,AlertDialog
的builder
中并没有抽象建造者(Builder
)、Director
(指挥者类)等角色。AlertDialog.Builder
同时扮演了Builder
、ConcreteBuilder
、Director
等角色,这是Android源码中使用构建者模式的一种简化。