设计模式

316 阅读12分钟

设计模式

一个讲设计模式讲的比较好的网站 UML

概述

设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。

七大原则

单一职责原则 (Single Responsibility Principle)

一个类应该只有一个发生变化的原因。即一个类只负责一项职责

例如:某个类A负责两个不同的职责,职责A1和职责A2,那么当职责A1需求发生改变而需要修改类A,有可能会导致原来运行正常的职责A2功能发生故障。

开放-关闭原则 (Open-Closed Principle)--总原则

一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭

例如:这里有个生产电脑的公司,根据输入的类型,生产出不同的电脑。

里氏替换原则 (Liskov Substitution Principle)

所有引用基类的地方必须能透明地使用其子类的对象。里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。

例如:有一功能P1, 由类A完成,现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

依赖倒转原则 (Dependence Inversion Principle)

上层模块不应该依赖底层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。即面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

例如:某天产品经理需要添加新的功能,该功能需要操作数据库,一般负责封装数据库操作的和处理业务逻辑分别由不同的程序员编写。封装数据库操作可认为低层模块,而处理业务逻辑可认为上层模块,那么如果处理业务逻辑需要等到封装数据库操作的代码写完的话才能添加的话讲会严重拖垮项目的进度。 正确的做法应该是处理业务逻辑的程序员提供一个封装好数据库操作的抽象接口,交给低层模块的程序员去编写,这样双方可以单独编写而互不影响。

接口隔离原则 (Interface Segregation Principle)

客户端不应该依赖它不需要的接口。类间的依赖关系应该建立在最小的接口上。即每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

例如:类A、类B都实现接口I中的X、Y、Z方法,但是其实类A只需要用到X方法,类B只需要用到Y方法,我们应该把接口I拆成接口I1(只有方法X)、I2(只有方法Y)、I3(只有方法Z),然后让类A、类B去不同实现接口I1、I2

迪米特法则(Law Of Demeter)

又称为最少知道原则,它表示一个对象应该对其它对象保持最少的了解。通俗来说就是,只与直接的朋友通信,不跟“陌生人”通话。

例如:集团需要知道总公司、分公司的员工资料,总公司直接让总公司的员工填写相关资料,而分公司的员工资料,由总公司通知分公司,分公司让分公司的员工填写资料,然后分公司再提供给总公司,而不是总公司一个一个告知分公司的员工。

组合/聚合复用原则 (Composite/Aggregate Reuse Principle)

尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。即在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分; 新的对象通过向这些对象的委派达到复用已有功能的目的。

例如:公司里面有很多不同角色的员工,我们实际应用代码应该是人,然后角色继承人,然后不同角色的员工再去实现角色,而不能直接不同角色的员工直接继承人

记忆方法:单开里依接迪组

创建型模式

创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。

工厂方法模式

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

以Activity中的getSystemService()方法举例


   //根据传入的参数name决定创建哪个对象
    @Override
    public Object getSystemService(@ServiceName @NonNull 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);
    }

抽象工厂模式

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

    //Service就像是一个工厂,而onBind是工厂方法
    public class BaseService extends Service{
        @Nullable
        @Override
        public IBinder onBind(Intent intent){
            return new Binder();
        }
    }

生成器模式

生成器模式是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。也叫建造者模式、Builder模式。

        //通过一步一步创建icon、message而最终展示出来AlertDialog,还有经典的Okhttp也是 
        new AlertDialog.Builder(this)
                .setIcon(R.mipmap.ic_launcher)
                .setMessage(R.string.app_name)
                .show();

原型模式

原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。也叫克隆模式

        //复制intent对象
        Uri uri = Uri.parse("smsto:10000");
        Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
        Intent newIntent = (Intent) intent.clone();
        startActivity(newIntent);

单例模式

单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。


//上面提到过的getSystemService(),里面就用了单例模式
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);

结构型模式

结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

适配器模式

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。也叫封装器模式。

//典型的RecyclerView.Adapter,


桥接模式

桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。


例如:对于一个View来说,它有两个维度的变化,一个是它的描述比如Button、TextView等等他们是View的描述维度上的变化,另一个维度就是将View真正绘制到屏幕上,这跟Display、HardwareLayer和Canvas有关。这两个维度可以看成是桥接模式的应用。

组合模式

组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。


例如:Android中View的结构是树形结构,每个ViewGroup包含一系列的View,而ViewGroup本身又是View。这是Android中非常典型的组合模式。

装饰模式

装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。也叫装饰者模式、装饰器模式。


例如:Context是Android中一个几乎无处不在的角色,ContextWrapper/ContextThemeWrapper就在继承过程中承担了ContextImpl的装饰者角色。

外观模式

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。也叫门面模式。


例如:Context是最重要的一个类型。它封装了很多重要的操作,比如startActivity()、sendBroadcast()等,几乎是开发者对应用操作的统一入口。Context是一个抽象类,它只是定义了抽象接口,真正的实现在ContextImpl类中。

享元模式

享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。


例如:Handler消息机制中的Message消息池就是使用享元模式复用了Message对象。使用Message时一般会用到Message.obtain来获取消息。如果使用new Message()会构造大量的Message对象。

代理模式

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。也叫委托模式。


例如:当应用通过ActivityManager访问ActivityManagerService的接口方法时,本质上是通过IActivityManager生成的代理对象访问的,是一种典型的代理模式。

行为模式

行为模式负责对象间的高效沟通和职责委派。

责任链模式

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。


例如:在Android处理点击事件时,父View先接收到点击事件,如果父View不处理则交给子View,依次往下传递~

命令模式

命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。


例如:在Android事件机制中,底层逻辑对事件的转发处理。每次的按键事件会被封装成NotifyKeyArgs对象。通过InputDispatcher封装具体的事件操作。

迭代器模式

迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。


例如:Java中就有迭代器Iterator类,本质上说,它就是用迭代器模式。另外Android的Cursor也是。

中介者模式

中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。


例如:Binder机制中ServiceManager和Binder Driver充当中介者,协调服务端与客户端

备忘录模式

备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。


例如:Activity的onSaveInstanceState和onRestoreInstanceState就是用到了备忘录模式,分别用于保存和恢复。

观察者模式

观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。


例如:Android中ContentObserver,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理。

状态模式

状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。


例如:WIFI管理模块。当WIFI开启时,自动扫描周围的接入点,然后以列表的形式展示;当wifi关闭时则清空。这里wifi管理模块就是根据不同的状态执行不同的行为。

策略模式

策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。


例如:对Animation对象设置不同的插值器就可以实现不同的动画效果

模版方法模式

模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。


例如:Android中的Activity,整个执行过程其实就是一个框架是相同,具体的实现都是有子类来完成。

访问者模式

访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。


例如:Android 的编译时注解是一种访问者模式。编译时注解的核心原理依赖APT(Annotation Processing Tools)实现。

解释器模式

解释器模式是一种行为设计模式,给定一个语言,定义它的语法,并定义一个解释器,这个解释器用于解析语言。


例如:AndroidManifest.xml就定义了<Activity><Service>等标签(语句)的属性以及其子标签,规定了具体的使用(语法),通过PackageManagerService(解释器)进行解析。