设计模式总结
1.面向对象的六大原则:
1.1 解决类的耦合性:单一职责
定义:就一个类而言,应该只有一个职责。如果一个类有一个以上的职责,这些职责就耦合在了一起。
当一个职责发生变化时,可能会影响其它的职责。
原则:遵守单一职责原则,将不同的职责封装到不同的类或模块中。
1.2 解决程序可扩展性:开闭原则
定义:软件中的对象(类,模块,函数等等)对于扩展是开放的,对于修改是封闭的。
原则:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
1.3 解决程序的灵活性:里氏替换
核心原理是抽象。
定义:任何基类可以出现的地方,子类一定可以出现。子类可以扩展父类的功能,但不能改变父类原有的功能。
原则:
1、子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
2、子类中可以增加自己特有的方法
3、当子类重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松
4、当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等
1.4 解决程序稳定性:依赖倒置
其核心思想是:要面向接口编程,不要面向实现编程。
依赖倒置原则可以降低类间的耦合性。
依赖倒置原则可以提高系统的稳定性。
依赖倒置原则可以减少并行开发引起的风险。
依赖倒置原则可以提高代码的可读性和可维护性。
定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象
原则:
每个类尽量提供接口或抽象类,或者两者都具备。
变量的声明类型尽量是接口或者是抽象类。
任何类都不应该从具体类派生。
使用继承时尽量遵循里氏替换原则。
1.5 解决接口的耦合性:接口隔离
提高类的内聚性、降低它们之间的耦合性
定义:一个类对另一个类的依赖应该建立在最小的接口上。
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
原则:
接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
1.6 解决程序模块之间相对独立:迪米特法则
定义:
只与你的直接朋友交谈,不跟“陌生人”说话。
如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
降低了类之间的耦合度,提高了模块的相对独立性。由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
原则:
从依赖者的角度来说,只依赖应该依赖的对象。
从被依赖者的角度说,只暴露应该暴露的方法。
在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
在类的结构设计上,尽量降低类成员的访问权限。
在类的设计上,优先考虑将一个类设置成不变类。
在对其他类的引用上,将引用其他对象的次数降到最低。
不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
谨慎使用序列化(Serializable)功能。
2.单例模式具有以下几个优点:
1)在内存中只有一个对象,节省内存空间;
2)避免频繁的创建销毁对象,可以提高性能;
3)避免对共享资源的多重占用,简化访问;
4)为整个系统提供一个全局访问点。
场景:
需要频繁的进行创建和销毁的对象;
创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
工具类对象;
频繁访问数据库或文件的对象。
饿汉式:简单来说就是空间换时间,因为上来就实例化一个对象,占用了内存,(也不管你用不用)。绝对线程安全,立即加载。
(由于一个类在整个生命周期中只会被加载一次,因此该单例类只会创建一个实例)
场景:试用于创建对象次数多的场景。
//饿汉式单例
public class Singleton1 {
// 指向自己实例的私有静态引用,主动创建
private static Singleton1 singleton1 = new Singleton1();
// 私有的构造方法
private Singleton1(){}
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton1 getSingleton1(){
return singleton1;
}
}
懒汉式:简单来说就是时间换空间,于饿汉式正好相反。传统式的懒汉式单例是非线程安全的,延时加载。
场景:试用于创建对象次数少的场景。
// 懒汉式单例,非线程安全的
public class Singleton2 {
// 指向自己实例的私有静态引用
private static Singleton2 singleton2;
// 私有的构造方法
private Singleton2(){}
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton2 getSingleton2(){
// 被动创建,在真正需要使用时才去创建
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
推荐试用:
// 线程安全的懒汉式单例
同步延迟加载 — 使用内部类实现延迟加载
public class Singleton5 {
// 私有内部类,按需加载,用时加载,也就是延迟加载
private static class Holder {
private static Singleton5 singleton5 = new Singleton5();
}
// 私有的构造方法
private Singleton5() {}
public static Singleton5 getSingleton5() {
return Holder.singleton5;
}
}
/* Output(完全一致)
双重检测同步延迟加载
public class Singleton3 {
//使用volatile关键字防止重排序,因为 new Instance()是一个非原子操作,可能创建一个不完整的实例
private static volatile Singleton3 singleton3;
// 私有的构造方法
private Singleton3() {}
public static Singleton3 getSingleton3() {
// Double-Check idiom
if (singleton3 == null) {
synchronized (Singleton3.class) {
// 只需在第一次创建实例时才同步
if (singleton3 == null) {
singleton3 = new Singleton3();
}
}
}
return singleton3;
}
}/* Output(完全一致)
可以看到在getInstance3()方法里面,先判断当前实例是否为空,然后进入同步处理后又判断一次实例是否为空。前后两次判断校验了两次。这个就是双重校验
去掉第一个判断为空:即懒汉式(线程安全),这会导致所有线程在调用getInstance()方法的时候,不管三七二十一就直接排队等待同步锁,然后等到排到自己的时候进入同步处理时,才去校验实例是否为空,这样子做会耗费很多时间(即线程安全,但效率低下)。
去掉第二个判断为空:即懒汉式(线程不安全),这会出现 线程A先执行了getInstance()方法,同时线程B在因为同步锁而在外面等待,等到A线程已经创建出来一个实例出来并且执行完同步处理后,B线程将获得锁并进入同步代码,如果这时B线程不去判断是否已经有一个实例了,然后直接再new一个。这时就会有两个实例对象,即破坏了设计的初衷。(即线程不安全,效率高)
双重校验的目的:除了第一次实例化需要进行加锁同步,之后的线程只要进行第一层的if判断不为空即可直接返回,而不用每一次获取单例都加锁同步,因此相比前面两种懒汉式,双重检验锁更佳。(双重校验锁结合了 两种懒汉式 的优点)
3.MVVM:
https://www.cnblogs.com/loaderman/p/10076529.html
View: Activty/fragemnt,实际是View成还包括ViewDataBinding.
ViewModel:作为Activity/Fragment与其他组件的连接器。
负责转换和聚合Model中返回的数据,使这些数据易于显示,并把这些数据改变及时的通知给Activity/Fragment。
ViewModel是具有生命周期意识的,当Activity/Fragment销毁时ViewModel的onClear方法会被回调,你可以在这里做一些清理工作。
LiveData是具有生命周期意识的一个可观察的的数据持有者,ViewModel中的数据由LiveData持有,并且只有当Activity/Fragment处于活动时才会通知UI数据的改变,避免无用的刷新UI;
Model
Repository及其下方就是Model了。Repository负责提取和处理数据。数据可以来自本地数据库(Room),也可以来自网络,这些数据统一有Repository处理,对应隐藏数据来源及获取方式
Binder 绑定器
Android中的数据绑定技术由DataBinding和LiveData共同实现。当Activity/Fragment接收到来自ViewModel中的新数据时(由LiveData自动通知数据的改变),将这些数据通过DataBinding绑定到ViewDataBinding中,UI将会自动刷新,而不用书写类似setText的方法。
MVVM与MVC最大的区别就是:MVVM实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用d再自己手动操作来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。