从零开始学Android架构(一)——什么是设计模式?

本文介绍了Android架构中的设计模式,包括创建型和结构型设计模式。作者强调设计模式的重要性,并通过实例解释了工厂方法、抽象工厂、原型、建造者和单例模式的应用。文中还探讨了如何在Android中使用这些模式,如LayoutInflater、Transition、AlertDialog和Binder。设计模式是解决开发中常见问题的套路,有助于写出更优雅、解耦的代码。
摘要由CSDN通过智能技术生成

前言

不少人会觉得架构师是一个高大上的岗位,只有技术顶尖的人才能胜任,但其实它并没有这么高大上,大部分的架构师,都只是开发经验非常丰富,并且热爱学习,善于知识迁移和总结。应用的架构是一件非常成熟,有非常多的经验提供我们借鉴的事情,我们可以从Android的架构中学习大型项目的架构思路,我们也可以从Android的局部中学习框架的精髓,如binder的设计,framework的设计,我们也可以深入到Android的代码细节中去看看具体的实现,看看什么样的场景使用什么的设计模式,如何写出更优雅的代码。除了Android,我们还可以从Github或者其他的开源项目或者文章中学习各种优秀的架构思路,如分层架构,微内核架构,插件化架构等等。

我会通过几篇文章,来讲解我对架构的理解,文章主要为三个主题,分别介绍什么是架构,什么是框架,什么是设计模式

架构,框架,设计模式,这三点,是从项目的整体到代码的细节粒度不断缩小的过程。架构是一个高屋建瓴的角度,除了考虑如何解决大需求,还要考虑技术方案,可扩展性,性能,安全,成本等多个维度,用建房子来比喻,架构是敲定房屋风格,建筑材料,选定施工队,画好设计图纸的流程。当这些都确定后框架就上场了,框架用来解决局部如何设计,比如我们在业务功能开发时,是采用mvp还是mvvm的框架,在访问网络时,是采用Retrofit还是Okhttp等等,一个项目,涉及到了多个框架,框架犹如建房子时确定如何建造地基,如何进行脚手架搭建等局部流程,最后,就是具体编写代码时,采用什么样的设计模式,能够让代码更加的优雅,更加的解耦,这一点就像建房子时,安装窗户,如何安装墙砖等流程。

在这篇文章,我主要先讲第一个主题:什么是设计模式,这个主题也是最容易入手的,每一个开发新手,都可以在项目开发中,使用几个设计模式,即使没用过设计模式,也在代码中看到过各种各样的设计模式。

设计模式

我们在做事情时,很多事情都是有套路的,这个套路是前人总结的经验,什么情况下按照什么套路去做,一般都能把事情做的很顺利,写代码也不例外,在写代码的过程中,很多场景都有固定的写法套路,这个套路就是设计模式,设计模式是由GOF总结出的一套开发人员在实际开发中经常面临的场景的解决方案,设计模式并不是GOF发明的,GOF只是总结了这些套路,通过采用这些套路,也就是设计模式,我们写出的代码更加可靠性,解耦,复用性更高,也更容易被其他人理解,设计模式主要分为三种类型:创建型,结构型,行为型

我们先从创建型的设计模式开始,这也是最简单的设计模式。

创建型

创建型设计模式主要用来解决如何创建对象,虽然我们可以直接通过new的方式来创建一个对象,但是很多场景下,直接通过new创建对象并不是一种很好的方式,这时,使用创建型的设计模式就能很好的帮我们解决问题。创建型的设计模式包括了单例、原型、工厂方法、抽象工厂、建造者5种,他们的主要特点都是将对象的创建与使用分离。

下面就来看一下如何使用创建型的设计模式,什么场景下使用什么样的创建型设计模式。

工厂方法和抽象工厂

笔者曾经开发过一个分享的功能,弹起分享面板后,根据点击的分享图标,比如qq,微信,微博等等,需要创建对应的分享的对象实例。如果不用设计模式,就需要在每个图标的点击事件中去创建对应类型的分享实例,伪代码如下。

wxIcon.setOnClickListener(()->{
   
	WXShare wxShare = new WXShare;
	wxShare.share();
})

qqIcon.setOnClickListener(()->{
   
	QQShare qqShare = new QQShare;
	QQShare.share();
})

wbIcon.setOnClickListener(()->{
   
	WBShare wxShare = new WBShare;
	wbShare.share();
})

这样创建对象的方式有一种缺点:随着要分享的类型越来越多,需要直接创建的分享实例也越来越多,这意味这这个对象中需要打交道的对象也越来越多,这就违背了单一职责原则,分享面板的职责就是分享,创建各种类型的分享实例,不是分享面板的职责,上述的分享实例的创建还是很简单的,但是很多时候,对象的创建是很复杂的事情,需要初始化很多数据,所以直接在点击事件中创建分享的实例,不是一种比较好的方法。

上面的场景就可以用工厂方法来改造,改造步骤如下

  1. 首先定义分享接口以及实现分享接口的实体类
public interface IShare{
   
	void share();
}

public WXshare implements IShare{
   
	public void share(){
   
		//doSomething;
	}
}

public QQShare implements IShare{
   
	public void share(){
   
		//doSomething;
	}
}

public WBShare implements IShare{
   
	public void share(){
   
		//doSomething;
	}
}
  1. 定义工厂类ShareFactory
public class ShareFactory{
   
    public IShare createShareInstance(String shareType){
        
      if(shapeType.equalsIgnoreCase("WX")){
   
         return new WXshare();
      } else if(shapeType.equalsIgnoreCase("QQ")){
   
         return new QQShare();
      } else if(shapeType.equalsIgnoreCase("WB")){
   
         return new WBShare();
      }
      return null;
   }
}
  1. 使用工厂类型
ShareFactory shareFactory = new ShareFactory();

wxIcon.setOnClickListener(()->{
   
	IShare wxShare = shareFactory.createShareInstance("WX");
	wxShare.share();
})

qqIcon.setOnClickListener(()->{
   
	IShare qqShare = shareFactory.createShareInstance("QQ");
	QQShare.share();
})

wbIcon.setOnClickListener(()->{
   
	IShare wxShare = shareFactory.createShareInstance("WB");
	wbShare.share();
})

可以看到,使用工厂方法后,对象的创建工作就全部放在工厂方法对象中进行了,这也符合单一职责原则:对象创建,就让专门做这事的对象去做。

接下来我们在看看什么是抽象工厂,把他和工厂方法放在一起讲,是因为他们很相似,不同点是抽象工厂是由创建一种类型的对象实例,变成创建多种类型的对象实例。还是以我在上面提到了分享的例子,在开发完分享功能后,我又接了一个新的需求:接入第三方快捷登录。

在登陆界面中,当我点击qq的图标时,就进入qq的快捷登录,点击微信的图标时,就进入微信的快捷登录,点击微博的图标时,就进入微博的快捷登录。我们此时也可以使用工厂方法,为第三方登录的实例创建新建一个工厂方法,但是我们还有更好的创建方法:将登录的实例创建和分享的实例创建放在同一个工厂方法里面,这就是抽象工厂。为什么可以这样做呢?因为第三方分享和第三方登录时属于一个产品族,他们都引用了共同的第三方提供的SDK,并且这样做的好处也是为了符合单一职责原则。下面看看如何使用抽象工厂。

  1. 接着为登录创建接口以及实现类
public interface ILogin{
   
	void login();
}

public WXLogin implements Login{
   
	public void login(){
   
		//doSomething;
	}
}

public QQLogin implements Login{
   
	public void login(){
   
		//doSomething;
	}
}

public WBLogin implements Login{
   
	public void login(){
   
		//doSomething;
	}
}
  1. 为分享和登录创建抽象工厂
public abstract class AbstractFactory {
   
   public abstract IShare createShareInstance(String type);
   public abstract ILogin createLoginInstance(String type) ;
}
  1. 扩展抽象工厂,创建分享和登录的实体对象
public class ShareFactory extends AbstractFactory {
   
    
   @Override
   public IShare createShareInstance(String type){
   
      if(shapeType == null){
   
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
   
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
   
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
   
         return new Square();
      }
      return null;
   }
   
   @Override
   public ILogin createLoginInstance(String type) {
   
      return null;
   }
}

public class LoginFactory extends AbstractFactory {
   
    
   @Override
   public IShare createShareInstance(String type){
   
      return null;
   }
   
   @Override
   public ILogin createLoginInstance(String type) {
   
      if(color == null){
   
         return null;
      }        
      if(color.equalsIgnoreCase("RED")){
   
         return new Red();
      } else if(color.equalsIgnoreCase("GREEN")){
   
         return new Green();
      } else if(color.equalsIgnoreCase("BLUE")){
   
         return new Blue();
      }
      return null;
   }
}
  1. 创建一个工厂生成器,用来创建不同类型的工厂
public class FactoryProducer {
   
   public static AbstractFactory getFactory(String scene){
   
      if(scene.equalsIgnoreCase("Login")){
   
         return new LoginFactory();
      } else if(scene.equalsIgnoreCase("Share")){
   
         return new ShareFactory();
      }
      return null;
   }
}

此时,抽象工厂就创建好了,我们只需要在登录的场景时,通过FactoryProducer获取LoginFactory,就能创建各种类型的登录对象的实例;在分享的场景时,通过FactoryProducer获取ShareFactory,就能创建各种类型的分享对象实例。

使用场景总结

下面来总结一下工厂方法和抽象工厂的使用场景

工厂方法
  1. 根据不同的条件,需要创建不同的实例时,并且创建的对象比较复杂时,就可以使用工厂方法,上面的分享例子就是这样的场景,但是上面的例子中创建对象相对简单,实际使用中,如果创建的对象很简单,只需要new就能创建,而没有复杂的初始化,我们也可以直接创建对象,使用工厂方法会增加代码量和复杂度。
抽象工厂
  1. 抽象工厂的使用场景需要满足两个场景,第一是需要满足工厂方法的使用场景,即需要根据不同的条件,创建不同的实例,同时又要满足第二个场景,即需要有多个产品族的产品要创建。
Android应用实例——LayoutInflater

下面来看一个Android系统中使用工厂方法的例子:LayoutInflater,我们都使用过LayoutInflater的inflate来创建View,他创建View的流程如下

/frameworks/base/core/java/android/view/LayoutInflater.java

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
   
    final Resources res = getContext().getResources();
    final XmlResourceParser parser = res.getLayout(resource);
    try {
   
        return inflate(parser, root, attachToRoot);
    } finally {
   
        parser.close();
    }
}

inflate方法中会获取XmlResourceParser,这个类是专门用来解析xml布局文件的,然后调用inflate重载方法

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
   
    View result = root;

    try {
   
        int type;
        //循环读取xml文件,直到读取到结尾标志才跳出循环
        while ((type = parser.next()) != XmlPullParser.START_TAG &&
               type != XmlPullParser.END_DOCUMENT) {
   
            // Empty
        }


        final String name = parser.getName();

        //判断是否是merge标签,merge布局不许满足父view存在
        if (TAG_MERGE.equals(name)) {
   
            if (root == null || !attachToRoot) {
   
                throw new InflateException("<merge /> can be used only with a valid "
                                           + "ViewGroup root and attachToRoot=true");
            }

            //解析merge布局标签
            rInflate(parser, root, inflaterContext, attrs, false);
        } else {
   
            // 根据name,也就是解析出来的xml标签创建view
            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
            ……
        }

    } catch (XmlPullParserException e) {
   
        ……
    } catch (Exception e) {
   
        ……
    } finally {
   
        ……
    }
    return result;
}

我们直接看createViewFromTag函数,也就是创建View的函数,我们知道View的子类非常多,如TextView,ListView,ImageView等等,那么这里面是否是用的工厂方法呢?我们来看一下里面的实现

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
                       boolean ignoreThemeAttr) {
   
   ……

    try {
   
        View view;
        //调用factory来创建View
        if (mFactory2 != null) {
   
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
   
            view = mFactory.onCreateView(name, context, attrs);
        } else {
   
            view = null;
        }

        if (view == null && mPrivateFactory != null) {
   
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }

        //没有factory创建view,则调用自身的createView方法
        if (view == null) {
   
            final Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = context;
            try {
   
                if (-1 == name.indexOf('.')) {
                  
                    view = onCreateView(parent, name, attrs);
                } else {
   
                    //创建View
                    view = createView(name, null, attrs);
                }
            } finally {
   
                mConstructorArgs[0] = lastContext;
            }
        }

        return view;
    } catch (InflateException e) {
   
       ……
    } catch (ClassNotFoundException e) {
   
        ……
    } catch (Exception e) {
   
       ……
    }
}

View的创建,实际不是通过工厂方法创建的,而是根据解析出来的view标签,调用反射创建的,创建函数在createView中实现,为什么不用工厂方法呢?因为View的子类太多了,用工厂方法代码会非常的庞大,所以用反射代码会更简洁一点,关于createView的实现就不再这里说了,这里主要讲讲在createViewFromTag函数中出现的工厂,那么这里的factory有什么用呢?其实这里的factory是提供给开发自定义创建View的,如果我们xml中自定义了一个标签,比如Div,Android是无法创建Div这个标签对应的View,这个时候,我们就可以开发一个能根据Div这个标签创建对应的View的factory,然后将工厂set到LayoutInflater,这样LayoutInflater就能扩展创建Div这个xml标签了视图了。

通过这个例子我们可以看到,工厂方法或者抽象工厂,不仅仅可以让对象创建更符合单一职责,实现对象创建和使用的解耦,我们同样可以利用这种设计模式,提供一种扩展的能力,比如上面的例子就通过factory提供了创建自定义标签的试图的扩展能力。

原型

笔者曾经开发过一个画板的功能,画板可以画长方形,正方形,圆形等等,在画板上画画时,选择不同的形状,就要创建对应形状的对象实例,创建实例时需要初始化很多数据,比如颜色,画笔大小等等,相当的麻烦,这样的场景,我们可以用上面的工厂方法来进行优化,但是这里我不讲工厂方法,而是另外一种设计模式:原型模式,原型模式可以用来快速创建对象,让对象创建更加高效,下面看一下具体的实现。

  1. 创建形状的抽象,这些抽象类都需要继承Cloneable接口
public abstract class Shape implements Cloneable {
   
   
   private String id;
   protected  String type;
   private int color;
   private int paintSize;
   
   abstract void draw();
   
   public String getType(){
   
      return type;
   }
    
   public String setType(String type){
   
       this.type = type;
   }
   
   public String getId() {
   
      return id;
   }
   
   public void setId(String id) {
   
      this.id = id;
   }
    
   //省略其他属性初始化方法
   ……
       
   public Object clone() 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值