面向对象思想设计原则
在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则。
1. 单一职责原则
其实就是开发人员经常说的”高内聚,低耦合”。
也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。
在设计模式中,所有的设计模式都遵循这一原则。
例如:要实现逻辑和界面的分离。
简单通俗的来说:一个类只负责一项职责。
2. 开闭原则
核心思想是:一个对象对扩展开放,对修改关闭。
其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。
也就是说软件开发人员一旦写出了可以运行的代码,就不应该去改动它,而是要保证它能一直运行下去,如何能够做到这一点呢?这就需要借助于抽象和多态,即把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的。
3. 里氏替换原则
核心思想:在任何父类出现的地方都可以用它的子类来替代。
其实就是说:同一个继承体系中的对象应该有共同的行为特征。
通俗简单的说就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
4. 依赖注入(依赖倒置原则 )原则
核心思想:要依赖于抽象,不要依赖于具体实现。
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
通俗点说:要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
依赖倒置原则的核心思想是面向接口编程。
5. 接口分离原则
核心思想:不应该强迫程序依赖它们不需要使用的方法。
其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。
其原则字面的意思是:使用多个隔离的接口,比使用单个接口要好。
接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。
6. 迪米特原则(最少知道原则)
核心思想:一个对象应当对其他对象尽可能少的了解。
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用
通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
实际上,这些原则的目的只有一个:
- 降低对象之间的耦合,增加程序的可复用性、可扩展性、可维护性。
优秀的设计模式就是基于这些原则去实现的。
设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式不是一种方法和技术,而是一种思想
设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用
学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成
设计模式的分类
创建型模式: 对象的创建
结构型模式: 对象的组成(结构)
行为型模式: 对象的行为
有23种经典的设计模式,还涌现出其他的一些新的设计模式,比如:规格模式、对象池模式、雇工模式、黑板模式和空对象模式等等。后面有时间的话也会对这些模式进行分析。
创建型模式
创建型模式简单来说就是用来创建对象的。一共有五种:单例模式、建造者模式、工厂方法模式、抽象工厂模式、原型模式。
单例模式 :确保某一个类只有一个实例,并且提供一个全局访问点。
建造者模式 : 用来创建复杂的复合对象。
工厂方法模式 :让子类来决定要创建哪个对象。
抽象工厂模式 :创建多个产品族中的产品对象。
原型模式 :通过复制原型来创建新对象。
行为型模式
行为型模式主要是描述类或者对象是怎样交互和怎样分配职责的。一共有十一种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
策略模式 :封装不同的算法,算法之间能互相替换。
状态模式 :根据不同的状态做出不同的行为。
责任链模式 :将事件沿着链去处理。
观察者模式 :状态发生改变时通知观察者,一对多的关系。
模板方法模式 :定义一套流程模板,根据需要实现模板中的操作。
迭代器模式 :提供一种方法顺序访问一个聚合对象中的各个元素。
备忘录模式 :保存对象的状态,在需要时进行恢复。
访问者模式 :稳定数据结构中,定义新的操作行为。
中介者模式 :将网状结构转变为星型结构,所有行为都通过中介。
解释器模式 :定义语法,并对其进行解释。
命令模式 :将请求封装成命令,并记录下来,能够撤销与重做。
结构型模式
结构型模式主要是用于处理类或者对象的组合。一共有七种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
代理模式 :控制客户端对对象的访问。
组合模式 :将整体与局部(树形结构)进行递归组合,让客户端能够以一种的方式对其进行处理。
适配器模式 :将原来不兼容的两个类融合在一起。
装饰者模式 :为对象添加新功能。
享元模式 :使用对象池来减少重复对象的创建。
外观模式 :对外提供一个统一的接口用来访问子系统。
桥接模式 :将两个能够独立变化的部分分离开来。
观察者模式
1.观察者模式的定义
观察者(Observer)模式又名发布-订阅(Publish/Subscribe)模式。GOF给观察者模式如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
观察者模式属于行为型模式。
2.模型图:
3.该模式包含四个角色:
抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。
4.优点
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
5.缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
5.使用场景例子:
有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息,微信用户就是观察者,微信公众号是被观察.
6.具体案例
抽象被观察者
/**
* 被观察者接口
* 声明了添加、删除、通知观察者方法
* Created by xiaoyehai on 2017/1/23.
*/
public interface IObservable {
/**
* 注册观察者
*
* @param observer
*/
void registerObserver(IObserver observer);
/**
* 注销观察者
*
* @param observer
*/
void unregisterObserver(IObserver observer);
/**
* 通知观察者
* @param s
*/
void notifyObserver(String s);
}
具体被观察者:
/**
* 被观察者
* Created by xiaoyehai on 2017/1/23.
*/
public class ConcreteObservable implements IObservable {
/**
* 观察者集合
*/
private List<IObserver> list = new ArrayList<>();
@Override
public void registerObserver(IObserver observer) {
list.add(observer);
}
@Override
public void unregisterObserver(IObserver observer) {
list.remove(observer);
}
@Override
public void notifyObserver(String s) {
//遍历观察者,通知所有观察者被观察者发生变化了
for (IObserver observer : list) {
observer.update(s);
}
}
}
抽象观察者:
/**
* 观察者接口
* 定义了一个update()方法,当被观察者调用notifyObservers()方法时,观察者的update()方法会被回调。
* Created by xiaoyehai on 2017/1/23.
*/
public interface IObserver {
void update(String s);
}
具体观察者:
/**
* 具体观察者
* Created by xiaoyehai on 2017/1/23.
*/
public class ConcreteObserver implements IObserver {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String s) {
System.out.println(name + "收到消息:" + s);
}
}
编写一个测试类:
**
* 观察者模式:
* 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
* 这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
* <p>
* 推模型
* Created by xiaoyehai on 2017/1/23.
*/
public class Test {
public static void main(String[] args) {
//创建被观察者
IObservable observable = new ConcreteObservable();
//创建观察者
IObserver observer1 = new ConcreteObserver("观察者1");
IObserver observer2 = new ConcreteObserver("观察者2");
IObserver observer3 = new ConcreteObserver("观察者2");
//注册观察者
observable.registerObserver(observer1);
observable.registerObserver(observer2);
observable.registerObserver(observer3);
//通知观察者
observable.notifyObserver("被观察者状态变化了");
}
}
结果:
观察者1:被观察者状态变化了
观察者2:被观察者状态变化了
观察者3:被观察者状态变化了
7.推模型和拉模型
观察者模式分为推模型和拉模型
推模型:
主题对象向观察者推送主题的详细信息(参数),不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
拉模型:
主题对象在通知观察者的时候,传递一个主题对象自身的引用,观察者根据需要从主题对象引用中“拉取”需要的数据。
区别:
推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,观察者自己去按需要取值。
推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者重构观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。
上面的例子就是推模型,现在讲下拉模型,拉模型通常都是把主题对象当作参数传递
抽象被观察者:
public interface Subject {
//注册观察者
public void addObserver(Observer observer);
//移除观察者
public void removeObserver(Observer observer);
//通知观察者
public void notifyObserver();
具体观察者:
/**
* 具体的被观察者
* Created by xiaoyehai on 2016/11/29.
*/
public class ConcreteSubject implements Subject {
private int subjectState;
public int getSubjectState() {
return subjectState;
}
//存放观察者的集合
private List<Observer> list = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
list.add(observer);
}
@Override
public void removeObserver(Observer observer) {
list.remove(observer);
}
@Override
public void notifyObserver() {
//遍历观察者,通知所有观察者被观察者发生变化了
for (Observer observer : list) {
observer.update(this);
}
}
}
抽象观察者:
public interface Observer {
//当被观察者发生变化时,更新观察者, 传入主题对象
public void update(Subject subject);
}
具体观察者:
/**
* 具体的观察者
* Created by xiaoyehai on 2016/11/29.
*/
public class ConcreteObserver implements Observer {
//观察者的状态,让他和被观察者的状态保持一致
private int observerState;
@Override
public void update(Subject subject) {
observerState = ((ConcreteSubject) subject).getSubjectState();
System.out.println("观察者的状态:" + observerState);
}
}
测试类:
/**
* 观察者模式:拉模型
* 拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,
* 干脆把自身传递给观察者,让观察者自己去按需要取值
* Created by xiaoyehai on 2016/11/29.
*/
public class Test {
public static void main(String[] args) {
//创建被观察者
Subject subject = new ConcreteSubject();
//创建观察者
Observer observer1 = new ConcreteObserver();
Observer observer2 = new ConcreteObserver();
Observer observer3 = new ConcreteObserver();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.addObserver(observer3);
//通知所有观察者,被观察者变化了
subject.notifyObserver();
}
}