Android开发常用的设计模式 您所在的位置:网站首页 安卓app开发模式是什么 Android开发常用的设计模式

Android开发常用的设计模式

2024-05-27 18:22| 来源: 网络整理| 查看: 265

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(Classc){//所有的产品必须实现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{ 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 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具体方法之前和之后插入一些操作。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有