设计模式的理解与总结

2020-02-24  本文已影响0人  郑土强ztq

从代码复用说起

如果有一段代码逻辑是会被很多函数使用到,最低级的做法是在每个函数里面重复的写这段代码逻辑。为了代码可以被复用,我们就可以将其抽取成一个函数。

函数接收输入参数,经过内部逻辑处理后返回结果(不一定有返回值,如void)。

想要执行这段代码逻辑只需要进行函数调用就可以了。这个做法的范围是在同一个文件(类)中的,粒度是函数。

如果把一些函数集中在一个地方,这就是一个类了。这个类有它自己的职责,如果新类还有别的事情(职责)要做,那就继续新建一个类把这些逻辑抽取出来。想要调用某个函数只要通过对象去调用就可以了。这种做法的范围是类级别,粒度是类。这也是设计模式的单一职责原则。(注意也可以是一开始就创建一个类,然后根据这个类的职责去编写相关的代码;不一定非得是从现有的代码中去抽取函数放在类里面,那是重构)。更加直接的说法就是,把一些相关联的函数写在一个类里就是单一职责,其他相关性不大的就继续放在另一个类。

类的设计与设计模式的原则

Java是一门面向对象的语言,具有封装,继承,多态的特性。代码复用分离逻辑达到解耦单一职责以及设计一套机制(如Handler机制)要求封装类去进行功能组织。要使用一个类,需要实例化new一个对象,而对象可以被引用持有(经常混为一谈),通过对象(或引用).函数的形式调用函数。封装成类带来两个好处:可以使用继承和多态的特性。继承的优点是可以复用父类的代码以及面向父类(接口)编程。指向父类的引用可以指向子类,调用时会产生多态的效果,最终调用的是引用真正指向的对象。

class A{
    void func1();
}
class B extends A{
    @override
    void func1();
    void func2();
}
class C extends B{
    @override
    void func2();
    void func3();
}

指向子类的引用可以调用父类的函数,因为已经继承过来了。B b可以访问A类的函数。反之,父类的引用不能调用子类的函数。声明一个指向A的引用A a,通过a只能访问A类有定义的函数,是不能访问子类的函数的。如果子类覆写了父类的函数,通过父类的引用调用时将会产生多态的效果,将会调用父类引用真正指向的子类对象的函数。如A a = new B(),那么a.func1()调用的就是B覆写的版本。

接口隔离(最小接口)原则就是:声明期待的最小类就够了。例如我只需要使用A类的函数func1就够了,那么我会声明一个对象A a,此时a可以指向它的子类的实例,如a = new B()。这样带来的好处是,a可以接收的实例范围扩大了,只要别的类跟A有继承关系的,a都可以指向该类的实例。典型的场景是假如需求变更了,func1的功能变化了,那么我只要重新实现一个新的继承A类的D类,那么直接a = new D(),对于使用到a的地方来说替换是无感的,因为它本来就只期待A类的func1,而D类就有根据新需求编写的func1函数。

面向父类(接口)编程就是尽量使用父类引用,这样就可以实施开闭原则里氏代换原则依赖反转原则了。这几个原则都是依靠父类引用可以指向子类实例这一特性。

还有一个迪米特原则其实也可以说是单一职责原则的延伸:我应该只用跟某个类H去交互,H是如何去跟别的类去交互来实现功能的我不需要也不应该关心。我只需要知道我调用H的某一个函数有什么作用与效果就够了。

总结:单一职责与代码复用(别的地方需要使用类就通过对象.函数就可以了,与复用父类代码是两回事)要求封装类,有类了就可以考虑是否使用继承。使用继承的话就可以复用父类代码与面向父类(接口)编程,实施开闭原则,里氏代换原则,依赖反转原则了。通过最小接口原则去声明对象,通过迪米特原则去设计类。

设计类就是在单个类(XXX.java文件)里面应用设计模式;
使用类就是通过对象.函数去调用。

一句话设计模式之如何设计类

创建型设计模式


行为型设计模式


策略模式与状态模式的区别在于,策略模式只有一个函数;状态模式有多个函数。其实是很灵活看待的,反正本质上是面向基类编程。

public interface ClickListener{
    void onClick();
}

让类A持有回调接口的引用,同时提供registerListener与unRegisterListener函数接收观察者。事件发生时A就回调观察者的回调函数。典型场景有View的点击事件,异步回调(因为多线程不能直接通过返回值返回结果)。——观察者模式

结构型设计模式


更好的使用别人写的类(源码阅读)

一些简洁的补充

设计模式原则的指导;

类,对象,引用;

继承层次中的引用与实例;

接口隔离原则与基类引用,期望的最小引用类,当然最后是看引用指向的实例。

基类引用利用多态与外部注入,可以随时无成本替换实例(开闭原则)。

抽取成类可以继承,多态,封装了;

非静态内部类好处与技巧;默认持有外部类的引用,这样的话可以直接在内部类调用外部类的函数了。一般非静态内部类就是专门用在某个类里面,不开放给其他类使用。外部如果要使用的话要这样实例化:

new 外部类().内部类()
//或者
外部类 a = new 外部类();
new a.内部类();

根据这个特点,当有非静态内部类对象存在时肯定也存在着外部类对象。这也是内存泄漏的一个场景。别人持有Activity的某个非静态内部类的引用,非静态内部类持有其外部类即Activity的引用,导致Activity内存泄漏不能被回收。

静态(代码组织的方式,像是全局变量,可又跟自己的类有联系),匿名内部类,匿名对象。

成员函数含有this指针,能调用肯定有实例存在;因为类中非静态成员函数必须要通过对象.函数的方式调用。

类外跟类内调用函数;没啥区别,因为类内调用类的函数其实隐含了this指针的,也就是实际上相当于this.函数(),也一样是对象.函数的形式。

函数调用的时序性,成员变量在构造函数初始化与外部注入初始化,否则空指针。

Activity开发不关心实例;平时开发时为什么可以直接在生命周期去开始写代码,而不用去关心Activity实例的创建?是因为Activity的创建是系统进行管理的,但是任何函数想要发挥作用需要被调用才行。所以生命周期函数onXXX()肯定是需要被调用,而系统用模板方法模式去实现这个需求了。创建Activity实例之后会调用performXXX(),里面会调用onXXX(),也就是调用我们自己Activity覆写的onXXX()函数。

虚拟机栈的展示,类的函数的行号。

类图只能描述结构关系,实际逻辑需要时序图去描述。

上一篇 下一篇

猜你喜欢

热点阅读