前言
android开发中,必要的了解一些设计模式又是非常有必要的。 Android开发的设计模式,基本设计思想源于java的设计模式
java的设计模式有N多种,据不完全统计,迄今为止,网络出现最频繁的大概有23种。
设计模式的出现就是为了高质量、易维护和复用性强的代码
什么是设计模式?
- 基本定义:设计模式(Design pattern)是一套被反复使用的代码设计经验的总结。
- 使用设计模式的目的是为了可重用代码、让代码更容易被他人理解。
- 设计模式是是软件工程的基石脉络,如大厦的结构一样。
- Design pattern的四大要素:模式名称(Name),问题(Question),解决方案(Solution),效果(Efftive)。
- OO(面向对象)的六大原则:单一职责原则,开闭原则,里氏替换原则,依赖倒置原则,接口隔离原则,迪米特原则。
- 单一职责原则:一个类中应该是一组相关性很高的函数,数据的封装。两个完全不一样的功能就不应该放在一个类中。
- 开闭原则:对修改封闭,对扩展放开。
- 里氏替换原则:抽象和继承;所有引用基类的地方必须能透明的使用其子类的对象。
- 依赖倒置原则:抽象不应该依赖细节,细节应该依赖抽象。
- 接口隔离原则:将大接口改成多个小接口。
- 迪米特原则:也称为最少知识原则,一个对象应该对另一个对象有最少的了解。
设计模式的分类
设计模式分为三种类型:
- 创建型模式5种:单例模式,抽象工厂模式,工厂模式,原型模式,建造者模式。
- 结构型模式7种:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式。
- 行为型模式11种:观察者模式,中介者模式,访问者模式,解释器模式,迭代器模式,备忘录模式,责任链模式,状态模式,策略模式,命令模式,模板模式。
创建型模式5种
1. 单例模式(Singleton Pattern)– Android常用模式
简介
保证一个类仅有一个实例,全局只有一个访问点。
对单例的实现可以分为两大类——懒汉式和饿汉式,他们的区别在于:
懒汉式:指全局的单例实例在第一次被使用时构建
饿汉式:指全局的单例实例在类装载时构建。
从它们的区别也能看出来,日常我们使用的较多的应该是懒汉式的单例,毕竟按需加载才能做到资源的最大化利用
摘录自这一篇聊一聊Java的单例
核心Code
实现方法详解看七种方式实现Singleton模式
/**
* 单例模式,使用静态内部类,线程安全(推荐)
*/
public static class Singleton5 {
private final static class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
private static Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
它利用了ClassLoader来保证了同步,同时又能让开发者控制类加载的时机。从内部看是一个饿汉式的单例,但是从外部看来,又的确是懒汉式的实现。
/**
* 静态内部类,使用枚举方式,线程安全(推荐)
*/
public enum Singleton6 {
INSTANCE;
public void whateverMethod() {
}
}
这是极简的写法,利用了创建枚举实例的过程是线程安全的。所以这种写法也没有同步的问题。但是enum比较耗资源,需要权衡。
/**
* 静态内部类,使用双重校验锁,线程安全(推荐)
*/
public static class Singleton {
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这是一种双检锁实例的方式,只在第一次实例化的时候进行加锁,并在在加锁前后都会对是否实例化了进行判定.
性能方面优于单检锁的形式.为了避免编译器对new Singleton()进行优化,instance变量加上volatile修饰,去除编译器优化的干扰.
已有示例
Android中的系统级服务都是通过容器的单例模式实现方式,以单例形式存在,减少了资源消耗。
2. 工厂模式(Factory Pattern)
简介
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
核心Code
麦当劳的点餐,可以点可乐,汉堡这可以使用Builder模式,也可以点套餐,这个可以认为是工厂模式
public class FactoryDemo {
public static void start() {
Order order = OrderFactory.createBigMacCombo();
System.out.println(order.makeOrder());
}
}
------------------------Factory
public class OrderFactory {
//创建一份巨无霸套餐
public static Order createBigMacCombo() {
return new Order.OrderBuilder()
.addBurger(new BigMac())
.addBeverage(new Coke())
.build();
}
}
--------------------------Order
public class Order {
private IBurgers mBurger;
private IBeverages mBeverages;
private Order(OrderBuilder builder){
mBurger = builder. mBurger;
mBeverages = builder. mBeverages;
}
public String makeOrder(){
StringBuilder sb = new StringBuilder();
if ( mBurger!= null) {
sb.append( mBurger.makeBurger()).append( " ");
}
if ( mBeverages!= null) {
sb.append( mBeverages.makeDrinking()).append( " ");
}
return sb.toString();
}
public static class OrderBuilder{
private IBurgers mBurger;
private IBeverages mBeverages;
public OrderBuilder(){
}
public OrderBuilder addBurger(IBurgers burgers){
this. mBurger = burgers;
return this;
}
public OrderBuilder addBeverage(IBeverages beverages){
this. mBeverages = beverages;
return this;
}
public Order build(){
return new Order( this);
}
}
}
---------------------------------Product
public class BigMac implements IBurgers {
@Override
public String makeBurger() {
return "巨无霸";
}
}
public class Coke implements IBeverages {
@Override
public String makeDrinking() {
return "可乐";
}
}
public interface IBeverages {
String makeDrinking();
}
public interface IBurgers {
String makeBurger();
}
已有示例
Android中,BitmapFactory用于从不同的数据源来解析、创建Bitmap对象
3. 抽象工厂模式(Abstract Factory Pattern)
简介
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
使用场景:一个对象族或者一组没有任何关系的对象都有相同的约束,都可以使用抽象工厂模式
- 当调用者需要一个产品时,直接传递一个参数给工厂,让工厂生产不同的产品。
- 这些产品实现了同样的接口。
- 调用者无需了解细节,只需要提要求(传参数)给工厂即可。
角色介绍:
- AbstractProduct: 抽象产品类
- ConcreteProductA : 产品的具体实现A
- ConcreteProductB : 产品的具体实现B
- AbstractFactory : 抽象工厂
- ConcreteFactory : 具体工厂实现
但是抽象工厂模式有个最大的缺点:产品族扩展非常困难,严重违反了开闭原则
核心Code
用户只需要告诉Factory要的产品,不需要关心产品是如何生产的。不过每增加一样产品,BaseAppFactory都要增加一个方法,然后所有的实现类都要修改
public class FactoryDemo {
public static void start() {
BaseAppFactory factory = new MacAppFactory();
BaseTextEditor textEditor = factory.createTextEditor();
textEditor.edit();
textEditor.save();
BaseImageEditor imageEditor = factory.createImageEditor();
imageEditor.edit();
imageEditor.save();
}
}
--------------------Factory
public class MacAppFactory extends BaseAppFactory {
@Override
public BaseTextEditor createTextEditor() {
return new MacTextEditor();
}
@Override
public BaseImageEditor createImageEditor() {
return new MacImageEditor();
}
}
public abstract class BaseAppFactory {
public abstract BaseTextEditor createTextEditor();
public abstract BaseImageEditor createImageEditor();
}
-----------------Products
public class MacImageEditor extends BaseEditor {
@Override
public void edit() {
System.out.println("图片处理编辑器,edit -- Mac版");
}
@Override
public void save() {
System.out.println("图片处理编辑器,save -- Mac版");
}
}
public class MacTextEditor extends BaseEditor {
@Override
public void edit() {
System.out.println("文本编辑器,edit -- Mac版");
}
@Override
public void save() {
System.out.println("文本编辑器,edit -- Mac版");
}
}
public abstract class BaseEditor {
public abstract void edit();
public abstract void save();
}
已有示例
Android底层对MediaPlayer的创建。
MediaPlayerFactory是Android底层为了创建不同的MediaPlayer所定义的一个类。
4. 原型模式(Prototype Pattern)– Android常用模式
简介
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。基本可以理解成实现了clone方法
使用场景:在系统中要创建大量的对象,这些对象之间具有几乎完全相同的功能,只是在细节上有一点儿差别
- 比如我们需要一张
-
的几种不同格式:ARGB_8888、RGB_565、ARGB_4444、ALAPHA_8等。
那我们就可以先创建一个ARGB_8888的Bitmap作为原型,在它的基础上,通过调用Bitmap.copy(Config)来创建出其它几种格式的Bitmap。
- 另外一个例子就是Java中所有对象都有的一个名字叫clone的方法,已经原型模式的代名词了。
核心Code
核心Code
注意像ListView这类的需要深度copy
public class Person implements Cloneable {
private String name;
private int age;
private double height;
private double weight;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
private ArrayList<String> hobbies = new ArrayList<String>();
public ArrayList<String> getHobbies() {
return hobbies;
}
public void setHobbies(ArrayList<String> hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", weight=" + weight +
'}';
}
@Override
public Object clone() {
Person person = null;
try {
person = (Person) super.clone();
person.name = this.name;
person.weight = this.weight;
person.height = this.height;
person.age = this.age;
person.hobbies = (ArrayList<String>) this.hobbies.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
}
已有示例
Android中的Bundle类,该类实现了Cloneable接口
public Object clone() {
return new Bundle(this);
}
public Bundle(Bundle b) {
super(b);
mHasFds = b.mHasFds;
mFdsKnown = b.mFdsKnown;
}
Intent类,该类也实现了Cloneable接口
@Override
public Object clone() {
return new Intent(this);
}
public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
......
}
使用的时候可以直接拷贝现有的Intent,再修改不同的地方,便可以直接使用。
Uri uri = Uri.parse("smsto:10086");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);
shareIntent.putExtra("sms_body", "hello");
Intent intent = (Intent)shareIntent.clone() ;
startActivity(intent);
开源库OkHttp中,也应用了原型模式。它就在OkHttpClient这个类中,它实现了Cloneable接口
@Override
public OkHttpClient clone() {
return new OkHttpClient(this);
}
private OkHttpClient(OkHttpClient okHttpClient) {
this.routeDatabase = okHttpClient.routeDatabase;
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
......
}
5. 建造者模式(Builder Pattern)– Android常用模式
简介
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
核心Code
———–优化前
- 参数的构造函数的最后面的两个参数含义不清,可读性不怎么好,可能需要点开查看源码
-
当有很多参数时,编写这个构造函数就会显得异常麻烦
Person p1=new Person(); Person p2=new Person(“张三”); Person p3=new Person(“李四”,18); Person p4=new Person(“王五”,21,180); Person p5=new Person(“赵六”,17,170,65.4);
public class Person { private String name; private int age; private double height; private double weight;
public Person() { } public Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } public Person(String name, int age, double height, double weight) { this.name = name; this.age = age; this.height = height; this.weight = weight; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } }
———–优化后
如果换一个角度,试试Builder模式,可读性可是嗖嗖的~
我们给Person增加一个静态内部类Builder类,并修改Person类的构造函数。
创建过程一下子就变得非常清晰了。对应的值是什么属性一目了然,可读性大大增强。
Person.Builder builder=new Person.Builder();
Person person=builder
.name("张三")
.age(18)
.height(178.5)
.weight(67.4)
.build();
public class PersonNew {
private String name;
private int age;
private double height;
private double weight;
private PersonNew(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.height = builder.height;
this.weight = builder.weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public static class Builder {
private String name;
private int age;
private double height;
private double weight;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder height(double height) {
this.height = height;
return this;
}
public Builder weight(double weight) {
this.weight = weight;
return this;
}
public PersonNew build() {
return new PersonNew(this);
}
}
}
已有示例
在Android中, Builder模式也是被大量的运用。
比如常见的对话框的创建AlertDialog.Builder,ImageLoader的初始配置。
AlertDialog.Builder builder=new AlertDialog.Builder(this);
AlertDialog dialog=builder.setTitle("标题")
.setIcon(android.R.drawable.ic_dialog_alert)
.setView(R.layout.myview)
.setPositiveButton(R.string.positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton(R.string.negative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
dialog.show();
java中有两个常见的类也是Builder模式:StringBuilder和StringBuffer
还有比较著名框架中:Gson中的GsonBuilder
GsonBuilder builder=new GsonBuilder();
Gson gson=builder.setPrettyPrinting()
.disableHtmlEscaping()
.generateNonExecutableJson()
.serializeNulls()
.create();
EventBus中也有一个Builder
EventBus(EventBusBuilder builder) {...}
OkHttp中的
Request.Builder builder=new Request.Builder();
Request request=builder.addHeader("","")
.url("")
.post(body)
.build();
private Response(Builder builder) {...}
各大框架中大量的运用了Builder模式。
小结:
- 定义一个静态内部类Builder,内部的成员变量和外部类一样
- Builder类通过一系列的方法用于成员变量的赋值,并返回当前对象本身(this)
- Builder类提供一个build方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder
- 外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值
设计模式专题
Github Code: https://github.com/vivianking6855/android-advanced/tree/master/DesignPattern
Reference
《android之大话设计模式》