聊聊”设计模式”



序言

设计模式的起源

  • 与很多软件工程技术一样,模式起源于建筑领域,软件工程只有短短的几十年,与已经拥有几千年底蕴的建筑工程相比,后者有太多值得学习和借鉴的地方
  • 对模式的定义可以抽象为在特定环境下,人们为了解决某类重复出现问题,而总结归纳出来的有效解决方案

软件设计模式的诞生

 
GoF(Gang of Four)将模式的概念引入软件工程领域,这标志着软件模式的诞生,1995年 GoF将收集和整理好的23种设计模式汇编成了一本名叫《设计模式》书,该书的出版也标志着设计模式正式成为面向对象软件工程的一个重要研究分支。

我理解的设计模式

  • 上边提到的“设计模式”,实际针对的是现代的面向对象语言(Smalltalk, C++, Java, C# 等)
  • 通俗来说,设计模式就是一些编程的套路,不是具体的攻防动作
  • 设计模式是很多前辈花费大量精力总结的经验,是经过检验的高效的一系列对象组合方式。
  • 开始我是不太想聊设计模式的,因为怕自己功力不够,而用力过猛,就会走火入魔,落个半身残疾,但最后背不住老大的谆谆教导,决定谈谈我理解的设计模式 

入门招式

在了解设计模式之前有必要再温习下基础知识,面向对象、UML图

面向对象

  • 面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后(高级)的产物
封装
  • 定义:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
  • 我的理解:功能都给你做好了,你不必去理解它是怎么写出来的,直接使用即可。
  • 如:房子、电脑、手机、饮料等等都可以认为是一次封装,而他们怎么做出来的,你不需要关心。
继承
  • 定义:继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
  • 如:拿猫和狗来说,它们都是动物,而且它们有一些共同点,比如:名字,年龄,声音,吃等。
多态
  • 定义:同一个行为具有多个不同表现形式或形态的能力
  • 如:春天的树,新枝嫩叶,冬天的树叶都枯萎了,说明树具有多态性

UML类图

UML类图是一种结构图,用于描述一个系统的静态结构。类图以反映类结构和类之间关系为目的,用以描述软件系统的结构,是一种静态建模方法。

类关系

类与类之间的关系主要有六种:继承、实现、组合、聚合、关联和依赖,这六种关系的箭头表示如下:

  • 1,继承:继承关系也称泛化关系(Generalization),用于描述父类与子类之间的关系。父类又称作基类,子类又称作派生类 
     
    例如:公交车、出租车和小轿车都是汽车

  • 实现:主要用来规定接口和实现类的关系 
     
    例如:汽车和轮船都是交通工具,而交通工具只是一个可移动工具的抽象概念,船和车实现了具体移动的功能

  • 组合关系:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门 
     
    例如:人由头部和身体组成,两者不可分割,共同存在

  • 聚合关系:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在 
     
    例如:公交车司机和工衣、工帽是整体与部分的关系,但是可以分开,工衣、工帽可以穿在别的司机身上,公交司机也可以穿别的工衣、工帽

  • 关联关系:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的 
     
    汽车和司机,一辆汽车对应特定的司机,一个司机也可以开多辆车

  • 依赖关系:是一种使用的关系,即一个类的实现需要另一个类的协助 
     
    汽车依赖汽油,如果没有汽油,汽车将无法行驶

总结:

  • 组合/聚合区别:组合中的各类同生死,不能单独存在;聚合中各类不是同生死,可单独存在
  • 关联/依赖区别::某个类以成员变量的形式出现在另一个类中,二者是关联关系;某个类以局部变量的形式出现在另一个类中,二者是依赖关系

  • 各种关系的强弱顺序: 
    继承= 实现 > 组合 > 聚合 > 关联 > 依赖

参考资料:

设计原则(六大原则)

1 开闭原则

1988年,勃兰特·梅耶(Bertrand Meyer)在他的著作《面向对象软件构造(Object Oriented Software Construction)》中提出了开闭原则(Open Close Principle),它的原文是这样:“Software entities should be open for extension,but closed for modification”。

  • 意思:软件模块应该对扩展开放,对修改关闭。
  • 举例:在程序需要进行新增功能的时候,不能去修改原有的代码,而是新增代码,实现一个热插拔的效果(热插拔:灵活的去除或添加功能,不影响到原有的功能)。
  • 目的:为了使程序的扩展性好,易于维护和升级。
2 里氏代换原则
  • 意思:里氏代换原则(Liskov Substitution Principle)是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
  • 举例:球类,原本是一种体育用品,它的衍生类有篮球、足球、排球、羽毛球等等,如果衍生类替换了基类的原本方法,如把体育用品改成了食用品(那么软件单位的功能受到影响),就不符合里氏代换原则。 
    *目的:对实现抽象化的具体步骤的规范。
3 依赖倒转原则
  • 意思:依赖倒转原则(Dependence Inversion Principle)即针对接口编程,而不是针对实现编程。
  • 举例:以计算机系统为例,无论主板、CPU、内存、硬件都是在针对接口设计的,如果针对实现来设计,内存就要对应到针对某个品牌的主板,那么会出现换内存需要把主板也换掉的尴尬。
  • 目的:降低模块间的耦合。
4 接口隔离原则
  • 意思:接口隔离原则(Interface Segregation Principle)即使用多个隔离的接口,比使用单个接口要好。
  • 举例:比如:登录,注册时属于用户模块的两个接口,比写成一个接口好。
  • 目的:提高程序设计灵活性。
5 最少知道原则(迪米特法则)

迪米特法则(Demeter Principle)也称最少知道原则,1987年秋天由美国Northeastern University的Ian Holland提出,被UML的创始者之一Booch等普及。后来,因为在经典著作《 The Pragmatic Programmer》而广为人知。

  • 意思:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

  • 举例:一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。

  • 目的:降低类之间的耦合,减少对其他类的依赖。
6 单一职责原则

单一职责原则( Single responsibility principle )由罗伯特·C·马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表示此原则是基于汤姆·狄马克(Tom DeMarco)和Meilir Page-Jones的著作中的内聚性原则发展出的

  • 意思:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。
  • 举例:该原则意思简单到不需要举例!
  • 目的:类的复杂性降低,可读性提高,可维护性提高。

总结

  • 对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。

参考资料:

设计模式分类

构建型模式

单例模式
  • 定义:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
  • 实际应用

    // 单例对象
    private static AdvertPresenter mInstance;
    /** * 私有化构造函数 */
    private AdvertPresenter(){
    }
    /** * 获取AdvertPresenter实例 * @return */
    public static AdvertPresenter getInstance() {
    if (mInstance == null) {
    synchronized (AdvertPresenter.class) {
    if (mInstance == null) {
    mInstance = new AdvertPresenter();
    }
    }
    }
    return mInstance;
    }
  • Android源码的使用

    //获取WindowManager服务引用,其内部就是通过单例的方式持有一个WindowManager并返回这个对象
    WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
  • 参考资料: 
    单例模式引起的内存泄漏 
    浅谈单例模式 
    PHP单例模式详细介绍 
    Php中的单例模式面面观

工厂模式
  • 定义:轻松方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程
  • 分类:简单工厂模式、工厂方法模式、抽象工厂模式
  • 1、简单工厂模式
  • 定义:建立一个工厂(一个函数或一个类方法)来制造新的对象。
  • 项目示例:

    public static Operation createOperate(string operate) {
    Operation oper = null;
    switch (operate)
    {
    case "+":
    {
    oper = new OperationAdd();
    break;
    }
    case "-":
    {
    oper = new OperationSub();
    break;
    }
    case "*":
    {
    oper = new OperationMul();
    break;
    }
    case "/":
    {
    oper = new OperationDiv();
    break;
    }
    }
    return oper;
    }
    }
  • Android源码中的使用:

    //在getSystemService方法中就是用到了简单工厂模式,根据传入的参数决定创建哪个对象,由于这些对象以单例模式提前创建好了,所以此处不用new了,直接把单例返回就好。
    public Object getSystemService(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);
    }
  • 2、工厂方法模式

  • 是定义一个创建产品对象的工厂接口,让其子类决定实例化哪一个类,将实际创建工作推迟到子类当中。
  • 项目示例:

    //抽象工厂类
    public abstract class Factory{
    public abstract Product createProduct();
    }
    //具体工厂类
    public class ConcreteFactory extends Factory{
    public Product createProduct(){
    return new ConcreteProductA();
    }
    }
    //抽象产品类
    public abstract class Product {
    public abstract void method();
    }
    //具体产品类
    public class ConcreteProduct extends Prodect {
    public void method(){
    System.out.println( "我是具体产品!");
    }
    }
  • Android源码中的使用:我们在开发中会用到很多数据结构,比如ArrayList,HashMap等。 

  • 参考资料: 
    JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)

结构型模式

装饰者模式
  • 定义:动态的给一个对象添加额外的职责,就增加功能来说,装饰模式比子类继承的方式更灵活。
  • 要点:装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为。 
    //超类:装饰者和被装饰者共同继承该超类
    public abstract class Component{
    public abstract void operate();
    }
    //被装饰者
    public class ConcreteComponent extends Component{
    public void operate(){
    //具体的实现
    }
    }
    //装饰者
    public class Decorator extends Component {
    private Component component;
    public Decorator(Component component){
    this.component = component;
    }
    public void operate(){
    operateA();
    component.operate();
    operateB();
    }
    public void operateA(){
    //具体操作
    }
    public void operateB(){
    //具体操作
    }
    }
    public static void main(String[] args) {
    // 使用普通功能类
    Component concreteComponent = new ConcreteComponent();
    Component decorator = new Decorator(concreteComponent);
    decorator.operate();
    }
    }
适配器模式

行为型模式

模板方法模式
  • 定义:定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定的步骤
  • 举个例子:个汽车启动的过程,每一种汽车启动的过程都基本是一样的流程,无非是这一过程中存在一些细小差别 
观察者模式
  • 定义:一个被观察者管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知。这通常通过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统
  • 举个例子:有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息 

  • 参考资料: 
    Java设计模式——行为型模式

总结

  • 这里仅仅是抛砖引玉,还是那句话“一千个人心中有一千个哈姆雷特”
  • 如果哪天你对设计模式有种相见恨晚的感觉,那说明你该去收获那些巨人们留下的思想了
  • 而这个过程中需要更多的是代码量的积累技术的提升(共勉)

题外话

写文档的工具:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值