搜文章
推荐 原创 视频 Java开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发
Lambda在线 > 全栈开发者中心 > 谈谈23种设计模式在Android项目中的应用(一)

谈谈23种设计模式在Android项目中的应用(一)

全栈开发者中心 2017-11-30


 前言

  本文将结合实际谈谈23种设计模式,每种设计模式涉及

  • 定义:抽象化的定义与通俗的描述,尽量说明清楚其含义与应用场景

  • 示例:如果项目中有使用过该模式,则会给出项目中的代码,否则会给出尽可能简单好理解的java代码

  • Android:该设计模式在Android源码框架中哪些地方有使用到

  • 重构:项目中是否存在可以用该模式进行重构的地方,如果有会给出重构前与重构后的代码或者思路

  用这种方式进行介绍设计模式,旨在结合每天都在接触的Android实际项目开发更好地理解设计模式,拉近与设计模式的距离,同时在实际开发与重构中,思考可以应用的重构手段与设计模式,既能保证写出复用性与可靠性更高的代码,也是对如何利用重构与设计模式这两大支柱进行优雅编程的最佳实践与总结。

谈谈23种设计模式在Android项目中的应用(一)
 

  同时一次性以这种方式介绍23种设计模式,也是出于既然要使用一个模式,那么就应该要先知道这么一个模式的想法,四人帮的《设计模式》也是对经验的总结,但是有巨人托着你上去,又何必自己再摸黑造梯子。

  重构不是本章的重点,因为这也是一个非常大的话题,这边只讨论实际项目中是否有存在一些能用设计模式进行改善的地方。

  关于重构,这边也有写了一篇博文 重构:改善既有代码的设计 ,基本列举了《重构:改善既有代码的设计》中的各项要点,后续还会继续将《重构》中的手法与设计模式应用到实际项目中,有所总结之后会再写几篇实际应用的博文。

 简介

  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。

 六大原则

  单一职责原则

  单一原则很简单,就是将一组相关性很高的函数、数据封装到一个类中。换句话说,一个类应该有职责单一。

  开闭原则

  开闭原则理解起来也不复杂,就是一个类应该对于扩展是开放的,但是对于修改是封闭的。在一开始编写代码时,就应该注意尽量通过扩展的方式实现新的功能,而不是通过修改已有的代码实现,否则容易破坏原有的系统,也可能带来新的问题,如果发现没办法通过扩展来实现,应该考虑是否是代码结构上的问题,通过重构等方式进行解决。

  里氏替换原则

  所有引用基类的地方必须能透明地使用其子类对象。本质上就是说要好好利用继承和多态,从而以父类的形式来声明变量(或形参),为变量(或形参)赋值任何继承于这个父类的子类。

  依赖倒置原则

  依赖倒置主要是实现解耦,使得高层次的模块不依赖于低层次模块的具体实现细节。怎么去理解它呢,我们需要知道几个关键点:

  • 高层模块不应该依赖底层模块(具体实现),二者都应该依赖其抽象(抽象类或接口)

  • 抽象不应该依赖细节

  • 细节应该依赖于抽象

  在我们用的Java语言中,抽象就是指接口或者抽象类,二者都是不能直接被实例化;细节就是实现类,实现接口或者继承抽象类而产生的类,就是细节。使用Java语言描述就是:各个模块之间相互传递的参数声明为抽象类型,而不是声明为具体的实现类;

  接口隔离原则

  类之间的依赖关系应该建立在最小的接口上。其原则是将非常庞大的、臃肿的接口拆分成更小的更具体的接口。

  迪米特原则

  一个对象应该对其他的对象有最少的了解.

  假设类A实现了某个功能,类B需要调用类A的去执行这个功能,那么类A应该只暴露一个函数给类B,这个函数表示是实现这个功能的函数,而不是让类A把实现这个功能的所有细分的函数暴露给B。

 设计模式

  单例模式

  定义

  确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。

  如果某个类,创建时需要消耗很多资源,即new出这个类的代价很大;或者是这个类占用很多内存,如果创建太多这个类实例会导致内存占用太多。上述情况下就应该使用单例模式

  实际应用

 // 单例对象
 private static AdvertPresenter mInstance;
 /**
  * 私有化构造函数
  */
 private AdvertPresenter(){
 }
 /**
  * 获取AdvertPresenter实例
  * @return
  */
 public static AdvertPresenter getInstance() {
     if (mInstance == null) {
         synchronized (AdvertPresenter.class) {
             if (mInstance == null) {
                 mInstance = new AdvertPresenter();
             }
         }
     }
     return mInstance;
 }

  Android

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

  其内部就是通过单例的方式持有一个WindowManager并返回这个对象

  重构

  项目中存在多次使用Random与Gson的操作,可以将Random与Gson对象封装成单例进行使用

  建造者模式

  定义

  将一个复杂对象的构造与它的表示分离,使得同样的构造过程可以创建不同的表示。

  主要是在创建某个对象时,需要设定很多的参数(通过setter方法),但是这些参数必须按照某个顺序设定,或者是设置步骤不同会得到不同结果。

  示例

  各类自定义Dialog

  Android

AlertDialog.Builer builder=new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
    .setTitle("title")
    .setMessage("message")
    .setPositiveButton("Button1",
        new DialogInterface.OnclickListener(){
            public void onClick(DialogInterface dialog,int whichButton){
                setTitle("click");
            }   
        })
    .create()
    .show();

  重构

  暂无

  原型模式

  定义

  用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

  可以在类的属性特别多,但是又要经常对类进行拷贝的时候可以用原型模式,这样代码比较简洁,而且比较方便。

  拷贝时要注意浅拷贝与深拷贝

  示例

        private HashMap
 
   
   
 
  
    
    
  getClonePointMap(Map
  
    
    
   
   map) {            HashMap 
    
    clone = new HashMap<>();            if (map != null) {                Iterator iterator = map.entrySet().iterator();                while (iterator.hasNext()) {                    Map.Entry entry = (Map.Entry) iterator.next();                    String key = (String) entry.getKey();                    PointBean pointBean = (PointBean) entry.getValue();                    if (pointBean != null) {                        //遍历map并将克隆对象放到新的map中                        clone.put(key, pointBean.clone());                    } else {                        clone.put(key, null);                    }                }            }            return clone;        } 
    
  
 
   
   
 

  Android

Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
//克隆副本
Intent copyIntent=(Intetn)shareIntent.clone();

  重构

  如果存在逐一去除某个对象的各项参数值,转而赋值给另一个对象身上时,便可使用原型模式

  工厂模式

  简单工厂模式

  定义

  建立一个工厂(一个函数或一个类方法)来制造新的对象。

  示例

public static Operation createOperate(string operate)
{
    Operation oper = null;
    switch (operate)
    {
        case "+":
            {
            oper = new OperationAdd();
            break;
            }
        case "-":
            {
            oper = new OperationSub();
            break;
            }
        case "*":
            {
            oper = new OperationMul();
            break;
            }
        case "/":
            {
            oper = new OperationDiv();
            break;
            }
    }
    return oper;
   }
}

  Android

public Object getSystemService(String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException("System services not available to Activities before onCreate()");
    }
    //........
    if (WINDOW_SERVICE.equals(name)) {
         return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    //.......
    return super.getSystemService(name);
  }

  在getSystemService方法中就是用到了简单工厂模式,根据传入的参数决定创建哪个对象,由于这些对象以单例模式提前创建好了,所以此处不用new了,直接把单例返回就好。

  重构

//重构前
public class AdvertPresenter {
    ...
    private void initAdvertManager() {
        String[] platforms = mAdConfig.getAllPlatforms();
        if (platforms != null && platforms.length > 0) {
            int platformSize = platforms.length;
            for (int i = 0; i < platformSize; i++) {
                String platform = platforms[i];
                if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {
                    FacebookAdvertManager fbAdManager = new FacebookAdvertManager();
                    mAdvertManager.put(AdvertConstant.AD_PLATFORM_FACEBOOK, fbAdManager);
                } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB)) {
                    AdMobAdvertManager adMobAdvertManager = new AdMobAdvertManager();
                    mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADMOB, adMobAdvertManager);
                } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB)) {
                    MopubAdvertManager mopubAdvertManager = new MopubAdvertManager();
                    mAdvertManager.put(AdvertConstant.AD_PLATFORM_MOPUB, mopubAdvertManager);
                } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {
                    AdxAdvertManager mopubAdvertManager = new AdxAdvertManager();
                    mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADX, mopubAdvertManager);
                }
            }
        }
    }
    ...
}
//重构后
public class BaseAdvertManager {
    ...
    public static BaseAdvertManager create(String platform) {
        if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {
            return new FacebookAdvertManager();
        } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB)) {
            return new MopubAdvertManager();
        } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {
            return new AdxAdvertManager();
        } else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB)) {
            return new AdMobAdvertManager();
        } else {
            ***return new NullAdvertManager();***//引入NULL对象
        }
    }
    ...
}
public class AdvertPresenter {
    ...
    private void initAdvertManager() {
        String[] platforms = mAdConfig.getAllPlatforms();
        if (platforms != null && platforms.length > 0) {
            int platformSize = platforms.length;
            for (int i = 0; i < platformSize; i++) {
                String platform = platforms[i];
                mAdvertManager.put(platform, BaseAdvertManager.create(platform));
            }
        }
    }
    ...
}

  工厂方法模式

  定义

  是定义一个创建产品对象的工厂接口,让其子类决定实例化哪一个类,将实际创建工作推迟到子类当中。

  示例

public abstract class Product {
    public abstract void method();
}

public class ConcreteProduct extends Prodect {
    public void method(){
        System.out.println("我是具体产品!");
    }
}

public  abstract class Factory{
    public abstract Product createProduct();
}

public class ConcreteFactory extends Factory{

    public Product createProduct(){
        return new ConcreteProductA();
    }
}

  Android

  我们在开发中会用到很多数据结构,比如ArrayList,HashMap等。我们先来看下Java中Collection部分的类集框架的简要UML图。

  我们知道Iterator是迭代器,用来遍历一个集合中的元素。而不同的数据结构遍历的方式是不一样的,所以迭代器的实现也是不同的。使用工厂方法模式将迭代器的具体类型延迟到具体容器类中,比较灵活,容易扩展。

public interface Iterable
 
   
   
 
  
    
    
  {    /**     * Returns an iterator over elements of type {@code T}.     *     * @return an Iterator.     */    Iterator
  
    
    
   
   iterator();    //省略部分代码 } 
  
 
   
   
 

  List和Set继承自Collection接口,Collection接口继承于Iterable接口。所以List和Set接口也需要继承并实现Iterable中的iterator()方法。然后我们常用的两个间接实现类ArrayList和HashSet中的iterator方法就给我们具体构造并返回了一个迭代器对象。

  我们找到ArrayList类,查看iterator方法的实现。

@Override
public Iterator
 
   
   
 
  
    
    
  iterator() {    return new ArrayListIterator(); }
 
   
   
 

  ArrayListIterator类型定义如下:

private class ArrayListIterator implements Iterator
 
   
   
 
  
    
    
  {    /** Number of elements remaining in this iteration */    private int remaining = size;    /** Index of element that remove() would remove, or -1 if no such elt */    private int removalIndex = -1;    /** The expected modCount value */    private int expectedModCount = modCount;    public boolean hasNext() {        return remaining != 0;    }    @SuppressWarnings("unchecked") public E next() {        ArrayList
  
    
    
   
   ourList = ArrayList.this;        int rem = remaining;        if (ourList.modCount != expectedModCount) {            throw new ConcurrentModificationException();        }        if (rem == 0) {            throw new NoSuchElementException();        }        remaining = rem - 1;        return (E) ourList.array[removalIndex = ourList.size - rem];    }    public void remove() {        Object[] a = array;        int removalIdx = removalIndex;        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }        if (removalIdx < 0) {            throw new IllegalStateException();        }        System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);        a[--size] = null;  // Prevent memory leak        removalIndex = -1;        expectedModCount = ++modCount;    } } 
  
 
   
   
 

  我们看到这个类实现了Iterator接口,接口的定义如下:

public interface Iterator
 
   
   
 
  
    
    
  {    boolean hasNext();    E next();    default void remove() {        throw new UnsupportedOperationException("remove");    }    default void forEachRemaining(Consumer action) {        Objects.requireNonNull(action);        while (hasNext())            action.accept(next());    } }
 
   
   
 

  基本的结构也分析完了,接下来对号入座,看一看具体是如何实现工厂方法模式的。

  Iterator————>Product ArrayListIteratorr————>ConcreteProduct

  Iterable/List————>Factory ArrayList————>ConcreteFactory

  工厂方法使一个类的实例化延迟到子类,对应着将迭代器Iterator的创建从List延迟到了ArrayList。这就是工厂方法模式。

  重构

  暂无

  抽象工厂模式

  定义

  为创建一组相关或者是相互依赖的对象提供一个接口,而不需要制定他们的具体类

  抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。

  示例

public abstract class AbstractProductA{
    public abstract void method();
}
public abstract class AbstractProdectB{
    public abstract void method();
}

public class ConcreteProductA1 extends AbstractProductA{
    public void method(){
        System.out.println("具体产品A1的方法!");
    }
}
public class ConcreteProductA2 extends AbstractProductA{
    public void method(){
        System.out.println("具体产品A2的方法!");
    }
}
public class ConcreteProductB1 extends AbstractProductB{
    public void method(){
        System.out.println("具体产品B1的方法!");
    }
}
public class ConcreteProductB2 extends AbstractProductB{
    public void method(){
        System.out.println("具体产品B2的方法!");
    }
}

public abstract class AbstractFactory{
    public abstract AbstractProductA createProductA();

    public abstract AbstractProductB createProductB();
}

public  class ConcreteFactory1 extends AbstractFactory{
    public  AbstractProductA createProductA(){
        return new ConcreteProductA1();
    }

    public  AbstractProductB createProductB(){
        return new ConcreteProductB1();
    }
}

public  class ConcreteFactory2 extends AbstractFactory{
    public  AbstractProductA createProductA(){
        return new ConcreteProductA2();
    }

    public  AbstractProductB createProductB(){
        return new ConcreteProductB2();
    }
}

  Android

  由于该模式存在的局限性,Android中很少有用到这个模式的地方,com.android.internal.policy包下的IPolicy有使用到这个模式,它是关于Android窗口,窗口管理,布局加载,以及事件回退Handler这一系列窗口相关产品的抽象工厂,但是其在源码中其实也只有一个具体的工厂实现。因为这部分结构较为复杂,代码量大,有兴趣的同学可以自己去查看相关资料或者阅读源码。

  与工厂方法模式对比

  使用
  • 不依赖于产品类实例如何被创建,组合和表达的细节;

  • 产品有多于一个的产品族,而系统只消费其中某一族的产品;

  • 同属于同一个产品族是在一起使用的;

  • 提供一个产品类的库,所有产品以同样的接口出现,从而使使用者不依赖于实现;

  区别
  • 抽象工厂是面向一个工厂方法的升级;

  • 抽象方法提供的是一个产品族,即多个产品等级结构,而工厂方法则是针对一个产品等级结构;

  • 抽象方法提供的产品是衍生自多个抽象或者接口,而工厂方法则衍生自同一个抽象或者接口;

  优点
  • 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。

  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

  缺点
  • 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。

  • (可以把示例中的AB当做等级,12当做族群,A1B1属于同一族群不同等级,当添加同一等级下新的产品时很方便,但是要添加不同等级的产品就会破坏“开闭原则”)

  由于抽象工厂不易于拓展新的产品族,所以这种设计模式,在提供对外部人员访问时,很少使用,也有人说抽象工厂方法模式是一种很“恶心”的设计模式,运用最为典范的一个是该模式最初的目的,也就是为了适应Unit和Windows两个操作系统下的视图而构建视图族,视图族有各自不同的实现;另一个就是Java连接数据库的操作中,对不同的数据库的操作而形成的的对象操作族,但是当再次更换数据时,所需要造成的接口的修改也十分麻烦,所以扩展性不好

  重构

  暂无

  策略模式

  定义

  有一系列的算法,将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化。

  示例

public abstract class BaseAdvertManager {
    protected abstract void doLoadAdvert();
}

public class FacebookAdvertManager extends BaseAdvertManager {
 @Override
    protected void doLoadAdvert() {
        Log.v(TAG, "加载Facebook广告");
    }
}

public class AdmobAdvertManager extends BaseAdvertManager {
 @Override
    protected void doLoadAdvert() {
        Log.v(TAG, "加载Admob广告");
    }
}

  Android

  Android在属性动画中使用时间插值器的时候就用到了策略模式。在使用动画时,你可以选择线性插值器LinearInterpolator、加速减速插值器AccelerateDecelerateInterpolator、减速插值器DecelerateInterpolator以及自定义的插值器。这些插值器都是实现根据时间流逝的百分比来计算出当前属性值改变的百分比。通过根据需要选择不同的插值器,实现不同的动画效果。

  重构

  暂无

via:http://linbinghe.com/2017/1646c9f3.html

版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《谈谈23种设计模式在Android项目中的应用(一)》的版权归原作者「全栈开发者中心」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注全栈开发者中心微信公众号

全栈开发者中心微信公众号:fsder-com

全栈开发者中心

手机扫描上方二维码即可关注全栈开发者中心微信公众号

全栈开发者中心最新文章

精品公众号随机推荐