1、创建型模式(Creational Pattern)
对类的实例化过程进行了抽象,
能够将软件模块中对象的创建和对象的使用分离。
为了使软件的结构更加清晰,
外界对于这些对象只需要知道它们共同的接口,
而不清楚其具体的实现细节,
使整个系统的设计更加符合单一职责原则。
创建型模式隐藏了类的实例的创建细节,
通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
包含模式
简单工厂模式(Simple Factory)
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
建造者模式(Builder)
原型模式(Prototype)
单例模式(Singleton)
2、简单工厂模式(Simple Factory Pattern)
又称为静态工厂方法(Static Factory Method)模式,
它属于类创建型模式。
可以根据参数的不同返回不同类的实例。
简单工厂模式专门定义一个类来负责创建其他类的实例,
被创建的实例通常都具有共同的父类。
----简单工厂模式包含如下角色:
Factory:工厂角色
工厂角色负责实现创建所有实例的内部逻辑
Product:抽象产品角色
抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
ConcreteProduct:具体产品角色
具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
----以登录功能来说,
假如应用系统需要支持多种登录方式如:口令认证、域认证
(口令认证通常是去数据库中验证用户,而域认证则是需要到微软的域中验证用户)。
那么自然的做法就是建立一个各种登录方式都适用的接口
//抽象产品Login
public interface Login {
//登录验证
public boolean verify(String name , String password);
}
//具体产品DomainLogin
public class DomainLogin implements Login {
@Override
public boolean verify(String name, String password) {
// TODO Auto-generated method stub
/**
* 业务逻辑
*/
return true;
}
}
//具体产品PasswordLogin
public class PasswordLogin implements Login {
@Override
public boolean verify(String name, String password) {
// TODO Auto-generated method stub
/**
* 业务逻辑
*/
return true;
}
}
//工厂类LoginManager
//根据调用者不同的要求,创建出不同的登录对象并返回。而如果碰到不合法的要求,会返回一个Runtime异常。
public class LoginManager {
public static Login factory(String type){
if(type.equals("password")){
return new PasswordLogin();
}else if(type.equals("passcode")){
return new DomainLogin();
}else{
/**
* 这里抛出一个自定义异常会更恰当
*/
throw new RuntimeException("没有找到登录类型");
}
}
}
//测试调用
public class Test {
public static void main(String[] args) {
String loginType = "password";
String name = "name";
String password = "password";
Login login = LoginManager.factory(loginType);
boolean bool = login.verify(name, password);
if (bool) {
/**
* 业务逻辑
*/
} else {
/**
* 业务逻辑
*/
}
}
}
3、工厂方法模式(Factory Method Pattern)
又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式
或者多态工厂(Polymorphic Factory)模式,
它属于类创建型模式。
工厂父类负责定义创建产品对象的公共接口,
而工厂子类则负责生成具体的产品对象,
这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,
即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
----工厂方法模式包含如下角色:
1.Product:抽象产品,工厂方法模式所创建的对象的超类,
也就是所有产品类的共同父类或共同拥有的接口。
在实际的系统中,这个角色也常常使用抽象类实现。
2.ConcreteProduct:具体产品,
这个角色实现了抽象产品(Product)所声明的接口,
工厂方法模式所创建的每一个对象都是某个具体产品的实例。
3.Factory:抽象工厂,担任这个角色的是工厂方法模式的核心,
任何在模式中创建对象的工厂类必须实现这个接口。
在实际的系统中,这个角色也常常使用抽象类实现。
4.ConcreteFactory:具体工厂,担任这个角色的是实现了抽象工厂接口的具体Java类。
具体工厂角色含有与业务密切相关的逻辑,
并且受到使用者的调用以创建具体产品对象。
----在灯这个品类下,有灯泡和灯管两种产品,
并且都实现了灯的通用方法:关灯和开灯。
在工厂类下,有各种生产具体产品的子工厂负责生产相应的两种灯具。
//抽象的产品接口ILight
public interface ILight
{
void TurnOn();
void TurnOff();
}
//具体的产品类:BulbLight
public class BulbLight implements ILight
{
public void TurnOn()
{
Console.WriteLine("BulbLight turns on.");
}
public void TurnOff()
{
Console.WriteLine("BulbLight turns off.");
}
}
//具体的产品类:TubeLight
public class TubeLight implements ILight
{
public void TurnOn()
{
Console.WriteLine("TubeLight turns on.");
}
public void TurnOff()
{
Console.WriteLine("TubeLight turns off.");
}
}
//抽象的工厂类
public interface ICreator
{
ILight CreateLight();
}
//具体的工厂类:BulbCreator
public class BulbCreator implements ICreator
{
public ILight CreateLight()
{
return new BulbLight();
}
}
//具体的工厂类:TubeCreator
public class TubeCreator implements ICreator
{
public ILight CreateLight()
{
return new TubeLight();
}
}
//客户端调用
static void Main(string[] args)
{
//先给我来个灯泡
ICreator creator = new BulbCreator();
ILight light = creator.CreateLight();
light.TurnOn();
light.TurnOff();
//再来个灯管看看
creator = new TubeCreator();
light = creator.CreateLight();
light.TurnOn();
light.TurnOff();
}
4、抽象工厂模式(Abstract Factory Pattern)
提供一个创建一系列相关或相互依赖对象的接口,
而无须指定它们具体的类。
抽象工厂模式又称为Kit模式,
属于对象创建型模式。
----抽象工厂模式包含如下角色:
AbstractFactory:抽象工厂
ConcreteFactory:具体工厂
AbstractProduct:抽象产品
Product:具体产品
----假设有一个移动终端工厂,
可以制造苹果系列的移动产品和三星系列的移动产品。
这个工厂下有两个子厂,
一个负责制造苹果系列的Pad和三星系列的Pad,
另一个负责制造苹果系列的手机和三星系列的手机。
这便是一个典型的抽象工厂的实例。
//抽象产品: 苹果系列
public interface Apple
{
void AppleStyle();
}
//抽象产品: 三星系列
public interface Sumsung
{
void BangziStyle();
}
//具体产品:iphone
public class iphone implements Apple
{
public void AppleStyle()
{
Console.WriteLine("Apple's style: iPhone!");
}
}
//具体产品:ipad
public class ipad implements Apple
{
public void AppleStyle()
{
Console.WriteLine("Apple's style: iPad!");
}
}
//具体产品:note2
public class note2 implements Sumsung
{
public void BangziStyle()
{
Console.WriteLine("Bangzi's style : Note2!");
}
}
//具体产品:tabs
public class Tabs implements Sumsung
{
public void BangziStyle()
{
Console.WriteLine("Bangzi's style : Tab!");
}
}
//抽象工厂
public interface Factory
{
Apple createAppleProduct();
Sumsung createSumsungProduct();
}
//手机工厂
public class Factory_Phone implements Factory
{
public Apple createAppleProduct()
{
return new iphone();
}
public Sumsung createSumsungProduct()
{
return new note2();
}
}
//pad工厂
public class Factory_Pad implements Factory
{
public Apple createAppleProduct()
{
return new ipad();
}
public Sumsung createSumsungProduct()
{
return new Tabs();
}
}
//客户端调用
public static void Main(string[] args)
{
//采购商要一台iPad和一台Tab
Factory factory = new Factory_Pad();
Apple apple = factory.createAppleProduct();
apple.AppleStyle();
Sumsung sumsung = factory.createSumsungProduct();
sumsung.BangziStyle();
//采购商又要一台iPhone和一台Note2
factory = new Factory_Phone();
apple = factory.createAppleProduct();
apple.AppleStyle();
sumsung = factory.createSumsungProduct();
sumsung.BangziStyle();
Console.ReadKey();
}
5、单例模式
作为对象的创建模式,
单例模式确保某一个类只有一个实例,
而且自行实例化并向整个系统提供这个实例。
1.懒汉式,线程不安全
懒汉式其实是一种比较形象的称谓。既然懒,
那么在创建对象实例的时候就不着急。
会一直等到马上要使用对象实例的时候才会创建,
因此在装载对象的时候不创建对象实例。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这段代码存在致命的问题。
当有多个线程并行调用 getInstance() 的时候,
就会创建多个实例。
也就是说在多线程下不能正常工作。
2.懒汉式,线程安全
为了解决上面的问题,
最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
虽然做到了线程安全,
并且解决了多实例的问题,但是它并不高效。
因为在任何时候只能有一个线程调用 getInstance() 方法。
但是同步操作只需要在第一次调用时才被需要,
即第一次创建单例实例对象时。
这就引出了双重检验锁。
3.双重检验锁
双重检验锁模式(double checked locking pattern),
是一种使用同步块加锁的方法。
程序员称其为双重检查锁,
因为会有两次检查 instance == null,
一次是在同步块外,一次是在同步块内。
为什么在同步块内还要再检验一次?
因为可能会有多个线程一起进入同步块外的 if,
如果在同步块内不进行二次检验的话就会生成多个实例了。
public static Singleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (Singleton.class) {
if (instance == null) { //Double Checked
instance = new Singleton();
}
}
}
return instance ;
}
instance = new Singleton()这句并非是一个原子操作,
事实上在 JVM 中这句话大概做了下面 3 件事情:
1).给 instance 分配内存
2).调用 Singleton 的构造函数来初始化成员变量
3).将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)。
但是在 JVM 的即时编译器中存在指令重排序的优化。
也就是说上面的第二步和第三步的顺序是不能保证的
,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。
如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,
这时 instance 已经是非 null 了(但却没有初始化),
所以线程二会直接返回 instance,
然后使用,然后顺理成章地报错。
只需要将 instance 变量声明成 volatile 就可以了。
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4.饿汉式 static final field
饿汉式其实是一种比较形象的称谓。
既然饿,那么在创建对象实例的时候就比较着急
于是在装载类的时候就创建对象实例。
这种方法非常简单,因为单例的实例被声明成 static 和 final 变量了,
在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。
public class Singleton{
//类加载时就初始化
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
缺点是它不是一种懒加载模式(lazy initialization),
单例会在加载类后一开始就被初始化,
即使客户端没有调用 getInstance()方法。
饿汉式的创建方式在一些场景中将无法使用:
譬如 Singleton 实例的创建是依赖参数或者配置文件的,
在 getInstance() 之前必须调用某个方法设置参数给它,
那样这种单例写法就无法使用了。
5.静态内部类 static nested class
这种方法也是《Effective Java》上所推荐的。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法仍然使用JVM本身机制保证了线程安全问题。
由于静态单例对象没有作为Singleton的成员变量直接实例化,
因此类加载时不会实例化Singleton,
第一次调用getInstance()时将加载内部类SingletonHolder,
在该内部类中定义了一个static类型的变量INSTANCE ,
此时会首先初始化这个成员变量,
由Java虚拟机来保证其线程安全性,
确保该成员变量只能初始化一次。
由于getInstance()方法没有任何线程锁定,
因此其性能不会造成任何影响。
由于 SingletonHolder 是私有的,
除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;
同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
6.枚举 Enum
public enum EasySingleton{
INSTANCE;
}
可以通过EasySingleton.INSTANCE来访问实例,
这比调用getInstance()方法简单多了。
创建枚举默认就是线程安全的,
所以不需要担心double checked locking,
而且还能防止反序列化导致重新创建新的对象。
6、建造者模式(Builder Pattern)
又可以称为生成器模式。
将一个复杂对象的构建与它的表示分离,
使得同样的构建过程可以创建不同的表示。
建造者模式是一步一步创建一个复杂的对象
,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,
用户不需要知道内部的具体构建细节。
建造者模式属于对象创建型模式。
----建造者模式包含如下角色:
Builder:抽象建造者
ConcreteBuilder:具体建造者
Director:指挥者
Product:产品角色
----在Android源码中最常用到的Builder模式就是AlertDialog.Builder,
使用该Builder来构建复杂的AlertDialog对象。
简单示例如下 :
//显示基本的AlertDialog
private void showDialog(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon);
builder.setTitle("Title");
builder.setMessage("Message");
builder.setPositiveButton("Button1",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button1");
}
});
builder.setNeutralButton("Button2",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button2");
}
});
builder.setNegativeButton("Button3",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
setTitle("点击了对话框上的Button3");
}
});
builder.create().show(); // 构建AlertDialog, 并且显示
}