简介
项目开发中发现问题、解决问题这个过程中会出现很多问题,比如重复出现、某个问题的遗留,这些问题的本质就是设计模式。今天记录设计模式的知识点。
内容
在java以及其他的面向对象设计模式中,类与类之间主要有6种关系,他们分别是:依赖、关联、聚合、组合、继承、实现。它们的耦合度依次增强。
依赖关系:
对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。
关联关系:
分为单向关联和双向关联。在java中,单向关联表现为:类A当中使用了类B,其中类B是作为类A的成员变量。双向关联表现为:类A当中使用了类B作为成员变量;同时类B中也使用了类A作为成员变量。
聚合关系:
是关联关系的一种,耦合度强于关联,他们的代码表现是相同的,仅仅是在语义上有所区别:关联关系的对象间是相互独立的,而聚合关系的对象之间存在着包容关系,他们之间是“整体-个体”的相互关系。
组合关系:
是一种耦合度更强的关联关系。存在组合关系的类表示“整体-部分”的关联关系,“整体”负责“部分”的生命周期,他们之间是共生共死的;并且“部分”单独存在时没有任何意义。
继承:
表示类与类(或者接口与接口)之间的父子关系。
实现:
表示一个类实现一个或多个接口的方法。
设计原则
要点 | 定义 | 描述 |
单一职责原则 | 不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 | 问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。 |
里氏替换原则 | 定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。 | 问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。 |
依赖倒置原则 | 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 | 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。 |
接口隔离原则 | 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。 | 问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。 |
迪米特法则 | 一个对象应该对其他对象保持最少的了解。 | 问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。 |
开闭原则 | 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 | 问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。 |
|
|
|
设计模式
要点 | 定义 | 描述 |
单例模式 | 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 | 单例模式注意事项: |
工厂方法模式 | 定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。 | 在工厂方法模式中,核心的工厂类不再负责所有的对象的创建,而是将具体创建的工作交给子类去做。这个核心类则摇身一变,成为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个类应当被实例化这种细节。 |
抽象工厂模式 | 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。 | 在以下情况下,适用于工厂方法模式: |
模版方法模式 | 定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤。 | 子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑。 |
建造者模式 | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 | 与抽象工厂的区别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造模式可以强制实行一种分步骤进行的建造过程。 |
代理模式 | 为其他对象提供一种代理以控制对这个对象的访问。 | 所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 |
原型模式 | 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。 | 在Java语言里深度克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。 |
中介者模式 | 用一个中介者对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互。 | 适用场景 |
命令模式 | 意图:将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或记录日志,以及支持可撤销的操作 | 常见应用: |
责任链模式 | 使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。 | 一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。 |
装饰模式 | 又名包装(Wrapper)模式,装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。 | 装饰模式与类继承的区别: |
策略模式 | 定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。 | 策略模式属于对象行为型模式,主要针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。通常,策略模式适用于当一个应用程序需要实现一种特定的服务或者功能,而且该程序有多种实现方式时使用。 |
适配器模式 | 基于现有类所提供的服务,向客户提供接口,以满足客户的期望。 | 适配器模式的优点 |
迭代器模式 | 提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。 | 在jdk中,与迭代器相关的接口有两个:Iterator 与 Iterable |
组合模式 | 将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 | 通过实现组合模式,调用者对组合对象的操作与对单一对象的操作具有一致性。调用者不用关心这是组合对象还是文件,也不用关心组合对象内部的具体结构,就可以调用相关方法,实现功能。 |
观察者模式 | 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。 | 在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。 |
门面模式 | 外部与一个子系统的通信必须通过一个统一的门面对象进行。 | 门面模式的优点: |
备忘录模式 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态。 | 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。 |
访问者模式 | 封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 | 访问者模式的优点 |
状态模式 | 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。 |
|
解释器模式 | 给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。 |
|
享元模式 | 复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。 | 享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最常见、最直观的就是内存的损耗。享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。 |
桥梁模式 | 将抽象和实现解耦,使得两者可以独立地变化。 | 桥梁模式在Java应用中的一个非常典型的例子就是JDBC驱动器。JDBC为所有的关系型数据库提供一个通用的界面。一个应用系统动态地选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。 |
|
|
|
项目
写了一个Android的项目体现23中设计模式,项目如图:
测试代码:
- public void onClickSingleMode(View view) { // 单例
- SingleMode.getInstance();
- }
- public void onClickFactoryMethodModel(View view) {// 工厂方法
- IProduct iProduct = new FactoryMethodModel();
- iProduct.productMethod();
- iProduct = new Tree();
- iProduct.productMethod();
- }
- public void onClickAbstractFactoryModel(View view) {// 抽象工厂
- AbstractFactoryModel.test();
- }
- public void onClickTemplateMethodModel(View view) {// 模版方法模式
- TemplateMethodModel.test();
- }
- public void onClickBuilderMode(View view) {// 建造者模式
- BuilderMode.test();
- }
- public void onClickProxyMode(View view) {// 代理模式
- ProxyMode.test();
- }
- public void onClickCloneMode(View view) {// 原型模式
- CloneMode.test();
- }
- public void onClickIntermediaryModel(View view) {// 中介者模式
- IntermediaryModel.test1();
- IntermediaryModel.test2();
- }
- public void onClickCommandMode(View view) {// 命令模式
- CommandMode.test();
- }
- public void onChainOfResponsibilityModel(View view) {// 责任链模式
- ChainOfResponsibilityModel.test();
- }
- public void onClickDecorativeMode(View view) {// 装饰模式
- DecorativeMode.test();
- }
- public void onClickStrategyMode(View view) {// 策略模式
- StrategyMode.test();
- }
- public void onClickIteratorModel(View view) {// 模式
- IteratorModel.test();
- }
- public void onClickCombinationMode(View view) {// 组合模式
- CombinationMode.test();
- }
- public void onClickObserverMode(View view) {// 观察者模式
- ObserverMode.test();
- }
- public void onClickWindowMode(View view) {// 门面模式
- WindowMode.test();
- }
- public void onClickMemoMode(View view) {// 备忘录模式
- MemoMode.test();
- }
- public void onClickVisitorMode(View view) {// 访问者模式
- VisitorMode.test();
- }
- public void onClickStateModel(View view) {// 状态模式
- StateModel.test();
- }
- public void onClickParserMode(View view) {// 解释器模式
- ParserMode.test();
- }
- public void onClickFlyweightMode(View view) {// 享元模式
- FlyweightMode.test();
- }
- public void onClickBridgeMode(View view) {// 桥梁模式
- BridgeMode.test();
- }
总结
如果设计模式在编码设计生涯中用得极少,主要原因是对设计模式的理解还不够,认识不到问题的存在。
因为不能正确的分析问题、认识问题,当然也不可能很好的解决问题了。