Android开发常用的设计模式

GoF出版的《设计模式:可复用面向对象软件的基础》一书中,共记录了23种设计模式,这些设计模式本质是对面向对象设计的封装、继承、多态的理解。app开发过程中用的比较多的有8种设计模式。

单例模式

单例模式是我们在开发种经常使用到的一种设计模式。单例模式创建的类在当前进程种只有一个实例,并有一个访问它的全局入口。

1.单例模式的优点
  • 内存中只有一个对象实例,节省了内存空间。
  • 避免了频繁创建实例带来的性能消耗。
  • 提供了一种全局访问入口,例如读取配置信息。
2.单例模式的缺点
  • 一般静态类不提供接口实现、抽象方法等功能,扩展能力差,修改的话只能在这个单例类里面进行。
  • 由于静态模式使用了static全局变量,所以涉及生命周期的引用,这样很容易引起内存泄露,例如传入了一个Activity类,这个时候我们需要 传入的是跟static生命周期一样长的Application Context,否则就不要使用单例模式,例如Dialog对话就不要使用单例模式。
3.单例模式适用场景
  • 对象需要保存一些状态信息。
  • 避免多重读写操作。例如多个实例读取了同一资源文件,后续涉及对这个资源文件写入同步的操作。

单例模式实现

//单例模式的实现很多中,这里展示一种最常用的实现。代码如下:
public class SingletonDemo {
    
    private static volatile SingletonDemo sInstance = null;
    private SingletonDemo(){}
    
    public static SingletonDemo getInstance(){
        if(sInstance == null){
            synchronized (SingletonDemo.class){
                if(sInstance == null){
                    sInstance = new SingletonDemo();
                }
            }
        }
        return sInstance;
    }
    
    public void  printSomething(){
        System.out.println("this is a singleton");
    }
}

这种写法的好处有以下几点。

  • 构造函数private,不能直接new对象,保证通过 getInstance方法来创建。
  • 由于不能直接new对象,所以getInstance方法必须是一个static方法;而静态方法不能访问非静态成员变量,所以这个实例变量也必须是static的。
  • 双重检查锁,使用volatile关键字,重排序被禁止,所有的写操作都将发生在读操作之前。适合于一写多读的场景(即一个线程进行写操作,多个线程进行读操作)
//使用静态内部类的方式实现
public class Singleton{
    private Singleton(){}
    public static Singleton getInstance(){
       return SingletonHolder.sInstance;
    }
    private static class SingletonHolder{//该类之所以是static是因为sInstance是静态的,如果不设为静态会报错
       private static final Singleton sInstance=new Singleton();//重点在于确定是哪个类的单例
    }
    public void printSomething(){
        System.out.println("this is a singleton");
    }
}

静态类

静态类大家应该很熟悉,用static修饰的方法或者变量,可以直接调用,方便快捷。

public class StaticDemo{
    public static void printSomething(){
        System.out.println("this is a singleton");
    }
}

这是一个最简单的静态类,提供一个静态方法printSomething()。

1.静态类的优点
  • 静态类的方法直接调用即可,无须new一个实例对象。
  • 静态类的性能较好,因为静态类的方法是在编译期间就绑定了的。
2.静态类的缺点
  • 静态类方法不能被复写,没有扩展性。
  • 静态类做不到懒加载。

单例类和静态类的选择

单例表现的是类,静态类表现的是方法。

  • 如果需要类的扩展能力,例如Override,选择单例模式。
  • 如果类比较重,需要考虑懒加载,选择单例模式。
  • 如果有状态信息维护需求,选择单例模式。
  • 如果有资源文件访问需求,选择单例模式。
  • 如果需要将一些方法集中在一起,选择静态类。

工厂模式

所谓的工厂,通俗来讲就是用来生产产品的地方。从代码的角度来说,产品就是一个个具体的类的实例对象,工厂也是一个实例对象,用来生产这些实例产品。工厂模式就是解决实例化对象的问题。

简单工厂

1.优点

工厂类承担创建所有产品的职责,只要有想要创建的产品实例,都可以在工厂类里面实现,工厂类号称“万能类”。

2.缺点
  • 只要新增一个产品,就会对工厂类进行修改,违反了设计模式中的“开闭原则”,即对修改关闭(新增产品需要修改工厂类),对扩展开放(没有扩展)。
  • 工厂类会随着产品种类的增多变得庞大,而且不易于管理和维护。
package com.brett.myapplication;


public class Main {
    private interface ICar{
        void move();
    }

    private static class Benz implements ICar{

        @Override
        public void move() {

        }
    }

    private static class BMW implements ICar{

        @Override
        public void move() {

        }
    }

    private static class SimpleFactory{
        public static ICar getCar(int carType){
            switch (carType){
                case 0:
                    return new Benz();
                case 1:
                    return new BMW();
            }
            return null;
        }
    }


    public static void main(String[] args) {
        ICar car = SimpleFactory.getCar(0);
        car.move();
    }
}

工厂方法

1.优点
  • 弱化一个工厂类通用的概念,将生产产品的职责交给各自的产品工厂去完成,也就是每一个产品都有一个工厂类,负责完成本身产品的生产。
  • 符合“开闭原则”,对修改关闭(无须修改工厂类),对扩展开放(新增产品对应的工厂类)。
2.缺点
  • 工厂方法实现了多个工厂类,相对简单工厂来说,使用起来更复杂。
  • 缺少形成产品族的功能,这个后续可在抽象工厂模式中解决。
package com.brett.myapplication;


public class Main {
    private interface ICar{
        void move();
    }
    
    private interface IFactory{
        ICar getCar();
    }

    private static class Benz implements ICar{

        @Override
        public void move() {

        }
    }

    private static class BMW implements ICar{

        @Override
        public void move() {

        }
    }
    
    private class BenzFactory implements IFactory{

        @Override
        public ICar getCar() {
            return new Benz();
        }
    }
    
    private class BMWFactory implements IFactory{

        @Override
        public ICar getCar() {
            return new BMW();
        }
    }

    public void get(){
        IFactory factory = new BenzFactory();
        ICar car = factory.getCar();
        car.move();
    }
}

3.工厂方法的实现:泛型
package com.brett.myapplication;

public class CarFactory {

    private interface ICar{
        void move();
    }

    private interface IFactory{
        ICar getCar();
    }

    private static class Benz implements ICar {

        @Override
        public void move() {

        }
    }

    private static class BMW implements ICar {

        @Override
        public void move() {

        }
    }
    
    public static ICar createCar(Class<? extends ICar>c){//所有的产品必须实现ICar接口
        try{
            return c.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    
    ICar bnw = CarFactory.createCar(BMW.class);
}
4.工厂方法的实现:Enum
 enum EnumCarFactory{
        Benz{
            @Override
            public ICar create(){
                return new Benz();
            }
        },
        BMW{
            @Override
            public ICar create(){
                return new BMW();
            }
        };
        
        public abstract ICar create(); 
    }
    
    private void create(){
        try {
            ICar ACar = EnumCarFactory.valueOf("Benz").create();
            ACar.move();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

抽象工厂

1.简介

抽象工厂由“产品族”的概念拓展而来。
一个产品不止一个功能,例如我们为用户制定一套出行方案,这个方案里面配备的由车辆,穿的衣服等,这些功能合在一起就成为“人群”这个产品的功能。如果只配备了车辆,那就跟工厂方法模式一样,只有一个功能,这是极端的情况。
所谓的抽象工厂指的是工厂不止生产某一具体的产品,而是能扩展到生产一系列的产品。

package com.brett.myapplication;

public class CarFactory {

    private interface ICar{
        void move();
    }
    
    private interface  IClothes{
        void wear();
    }
    
    private class Gucci implements IClothes{

        @Override
        public void wear() {
            
        }
    }
    
    private class Prada implements IClothes{

        @Override
        public void wear() {
            
        }
    }

    private interface IAbsFactory{
        ICar getCar();
        IClothes getClothes();
    }
    
    private class ZhangSan implements IAbsFactory{

        @Override
        public ICar getCar() {
            return new BMW();
        }

        @Override
        public IClothes getClothes() {
            return new Gucci();
        }
    }
    
    private class LiSi implements IAbsFactory{

        @Override
        public ICar getCar() {
            return new Benz();
        }

        @Override
        public IClothes getClothes() {
            return new Prada();
        }
    }

    private class Benz implements ICar {

        @Override
        public void move() {

        }
    }

    private class BMW implements ICar {

        @Override
        public void move() {

        }
    }
    
    void get(){
        IAbsFactory absFactory = new ZhangSan();
        ICar car = absFactory.getCar();
        car.move();
        IClothes clothes = absFactory.getClothes();
        clothes.wear();
    }
}

Builder模式

为什么要用Builder模式

Builder模式主要用于解决初始化类时(也就是new一个类的实例出来),类的构造函数种类过多且不易管理的问题。

Builder模式的实现

我们的想法是Student类的构造函数不要那么多,但是又要满足初始化Student类变量的需求。可以考虑设计一个内部类,这个内部类的参数跟Student类的参数一样,而Student类的构造函数的参数,我们就设定为这个内部类。因此,只需要将这个内部类的变量初始化即可。
内部类变量设定的时候,我们采用链式结构,这样可以通过setXX().setXX()形式一直写下去。

public class Student{
        private String name;
        private int age;
        private boolean sex;
        
        public Student(){}
        
        public static StudentBuilder newInstance(){
            return new StudentBuilder();
        }
        
        public static class StudentBuilder{
            private String name;
            private int age;
            private boolean sex;
            
            public StudentBuilder setName(String name){
                this.name = name;
                return this;
            }
            
            public StudentBuilder setAge(int age){
                this.age = age;
                return this;
            }
            
            public StudentBuilder setSex(boolean sex){
                this.sex = sex;
                return this;
            }
            
            public Student build(){
                return new Student();
            }
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", sex=" + sex +
                    '}';
        }
    }

模板模式

模板模式介绍

模板模式是一种基于代码复用的设计模式。具体实现需要架构师和开发人员之间进行合作。架构师构造好实现的流程和轮廓,开发人员则完成具体的实现过程。

父类抽象模板的作用如下。

  • 定义abstract限定符方法并交由子类实现。
  • 定义非private方法,延迟至子类实现。

子类实现模板的作用如下。

  • 实现父类的abstract方法。
  • 可以重写父类非private方法。
public abstract class Car{
        void startUp(){
            System.out.println("启动!");
        }
        
        abstract void move();//强制要求实现
        
        void stop(){
            System.out.println("熄火!");
        }
        
        public final void operation(){//定义为final,防止被重写
            startUp();
            move();
            stop();
        }
        
        public class BMW extends Car{

            @Override
            void move() {
                System.out.println("BWM move!!!");
            }
        }
        
        public class Benz extends Car{

            @Override
            void move() {
                System.out.println("Benz move!!!");
            }
        }

观察者模式

观察者模式,包括观察者和被观察者。观察者们将自己的需求告知被观察者,被观察者负责通知到观察者。

Java自带的观察者

package com.brett.myapplication;


import java.util.Observable;
import java.util.Observer;

public class Main {

    //被观察者
    public static class Server extends Observable{
        private int time;

        public Server(int time){
            this.time = time;
        }

        public void setTime(int time){
            if(this.time == time){
                setChanged();//一定要标注,表明数据变更,需要通知观察者
                notifyObservers(time);
            }
        }
    }

    public static class Client implements Observer{

        private String name;
        public Client(String name){
            this.name = name;
        }
        @Override
        public void update(Observable observable, Object o) {
            if(observable instanceof Server){
                // TODO
                System.out.println("变化了");
            }
        }
    }

    public static void main(String[] args) {

        Server server = new Server(2019);
        Client client1 = new Client("张三");
        Client client2 = new Client("李四");
        server.addObserver(client1);
        server.addObserver(client2);
        server.setTime(2020);

    }
}

注意,不需要观察者需要移除观察者

 if(server!=null){
    server.deleteObservers();
 }

自己实现观察者模式

具体可以参考这篇博客
常用设计者模式—观察者模式

适配器模式

在什么场合下需要用到适配器模式呢?
现有系统扩展,需要接入第三方系统,也就是接入第三方API:如Class有3个字段A、B、C,需要再添加外部类OuterClass的两个字段,而且还要再不影响当前Class的情况下添加。
适配器模式提供的解决方案如下。

  • 因为要兼容原有类,所以原有类需要面向接口编程,也就是要有原有类的接口实现。
  • 适配器的目的是兼容原有类,所以适配器也必须实现原有类的接口。
  • 适配器内部实现具体的适配方案。
    public interface IAmericanCharger{
        void charge4American();
    }
    
    public class AmericanCharger implements IAmericanCharger{

        @Override
        public void charge4American() {
            System.out.println("do American charge!");
        }
    }

    public interface IChineseCharger{
        void charge4Chinese();
    }

    public class ChineseCharger implements IChineseCharger{

        @Override
        public void charge4Chinese() {
            System.out.println("do American charge!");
        }
    }
    
    public class AmericanDevice{
        private IAmericanCharger iAmericanCharger;
        
        public AmericanDevice(IAmericanCharger iAmericanCharger){
            this.iAmericanCharger = iAmericanCharger
        }
        
        public void work(){
            iAmericanCharger.charge4American();
        }
    }
    
    public class Adapter implements IAmericanCharger{
        private IChineseCharger iChineseCharger;
        public Adapter(IChineseCharger iChineseCharger){
            this.iChineseCharger = iChineseCharger
        }

        @Override
        public void charge4American() {
            iChineseCharger.charge4Chinese();
        }
    }
    
    public void get(){
        IChineseCharger chineseCharger = new ChineseCharger();
        Adapter adapter = new Adapter(chineseCharger);
        AmericanDevice device = new AmericanDevice(adapter);
        device.work();
    }

策略模式

Android的每一种模块都有很多种解决方案,例如网络模块有okhttp、vollery等;图片模块有Glide、Picaso等。平时开发的时候可能就会选定一种模块,例如图片就用Glide,然后在项目的代码里面直接调用Glide的接口来完成图片的处理。
如果我们要更换另一种实现方式的话,通常的做法是:将项目种所有用到的原先模块的API,统一换成新引入的模块的API。这样不仅工程量巨大,还容易造成新的问题;而且新的模块的API也需要重新了解和熟悉。

策略模式的实现

根据设计模式的“开闭原则”,我们应该尽量做到对修改关闭。如果使用上述提到的解决方案,那么相当于业务跟模块之间强耦合了。
那么怎么解决这种“前耦合”呢?答案是面向接口编程。我们在使用模块功能的时候,尽量不要直接使用模块提供的API接口,而是使用我们定义的接口提供的方法。
具体的解决方案如下。

  • 定义一个接口,这个接口的方法就是我们项目里面所调用的。
  • 所有引用的模块都要实现这个接口,虽然模块本身有自己的API,但是我们现在不是直接使用模块的API,而是使用我们定义的接口。所以这个模块必须要实现我们定义的接口。
  • 提供一个使用类,通常是单例模式。这个使用类就是我们项目里面所直接调用的,这个使用类可以实现或不实现我们定义的接口。
  • 在使用类种指定所引用的第三方模块。
    这种解决方案的好处是,无论怎么替换第三方模块,项目使用到这个模块功能的地方都不需要改动,只需要在配置里面设定好使用的第三方模块即可。
 public interface ILogProcessor{
        void v(String log);
        void d(String log);
        void i(String log);
        void e(String log);
    }
    
    public class DefaultLogProcessor implements ILogProcessor{

        @Override
        public void v(String log) {
            Log.v("DefaultLogProcessor",log);
        }

        @Override
        public void d(String log) {
            Log.d("DefaultLogProcessor",log);
        }

        @Override
        public void i(String log) {
            Log.i("DefaultLogProcessor",log);
        }

        @Override
        public void e(String log) {
            Log.e("DefaultLogProcessor",log);
        }
    }

    //提供一个使用类
    public class LogLoader implements ILogProcessor{//可以不实现ILogProcessor,自定义对外的方法名
        private static volatile LogLoader sInstance = null;
        private static ILogProcessor sILogProcessor;
        
        private LogLoader(){}
        
        public static LogLoader getInstance(){
            if(sInstance == null){
                synchronized (LogLoader.class){
                    if(sInstance == null){
                        sInstance = new LogLoader();
                    }
                }
            }
            return sInstance;
        }
        
        //通过选定使用哪一个日志功能类
        public static ILogProcessor load(ILogProcessor logProcessor){
            return sILogProcessor = logProcessor;
        }

        @Override
        public void v(String log) {
            sILogProcessor.v(log);
        }

        @Override
        public void d(String log) {
            sILogProcessor.d(log);
        }

        @Override
        public void i(String log) {
            sILogProcessor.i(log);
        }

        @Override
        public void e(String log) {
            sILogProcessor.e(log);
        }
    }

代理模式

代理是一个中间者的角色,它屏蔽了访问方和委托方之间的直接接触。也就是说访问方不能直接调用委托方的这个对象,而是必须实例化一个跟委托方有同样接口的代理方,通过这个代理方完成对委托方的调用。
什么时候需要用到代理模式?

  • 访问方不想和委托方有直接接触,或者直接接触有困难。
  • 访问方对委托方的访问需要增加额外的处理,例如访问前和访问后都做一些处理。

代理模式有两类:静态代理和动态代理,具体实现方式可参考下面这篇博客。
设计模式之之代理模式

代理模式应用:简单工厂

class ProxyFactory<T>{
    private T client;//目标对象
    private IBefore before;
    private IAfter after;
    
    public void setClient(T client){
        this.client = client;
    }
    
    public void setBefore(IBefore before){
        this.before = before;
    }
    
    public void setAfter(IAfter after){
        this.after = after;
    }
    
    public <T> T createProxy(){
        ClassLoader loader = client.getClass().getClassLoader();
        Class[] interfaces = client.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if("getName".equals(method.getName())){
                    //可根据name值过滤方法
                }
                if(before != null){
                    before.doBefore();
                }
                Object result = method.invoke(client,objects);//执行目标对象的目标方法
                if (after != null){
                    after.doAfter();
                }
                return result;
            }
        };
        return (T) Proxy.newProxyInstance(loader,interfaces,h);
    }
    
    //调用
    void get(){
        ProxyFactory factory = new ProxyFactory();
        factory.setBefore(new IBefore() {
            @Override
            public void doBefore() {
                System.out.println("doBefore");
            }
        });
        factory.setClient(new Benz());
        factory.setAfter(new IAfter() {
            @Override
            public void doAfter() {
                System.out.println("doAfter");
            }
        });
        ICar car = (ICar)factory.createProxy();
        car.move();
    }
}

动态代理的应用:AOP(面向切面编程)

AOP的实现方式之一是动态代理。简单来说:AOP能够动态地将代码切入指定地位置,在指定位置上实现编程,从而达到动态改变原有代码地目的。上面的IBefore和IAfter接口实际上就是简单地实现了AOP,例如在invoke具体方法之前和之后插入一些操作。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值