面向对象设计原则及设计模式之创建型模式

一.设计模式概述

1.1模式

诞生:模式诞生于建筑业而非软件业。

定义:是在特定的环境下人们解决某类重复出现问题的一套成功或有效的解决方案

1.2设计模式

定义:是在特定的环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对对象和类之间的相互作用。

1.2.1设计模式的四个关键要素

1.模式名称:名称通常描述了问题,解决方案方便开发人员之间的交流。所以在学习过程中应该熟悉模式中英文名称。例如在己有的类库中:如果一个类的名称为XXXAdapter,则该类为一个适配器类,同理XXXFactory为一个工厂类
2.问题:描述了在什么情况下应该使用模式,它包含了设计中存在的问题以及问题存在的原因
3.解决方案:描述了设计模式的组成成员,以及成员之间的相互关系、各自的职责和协作方式。也就是说,模式是一个通用的模板,只提供问题的抽象描述,怎么使用一组有意义的元素来解决问题。
4.效果:没有一个设计模式是百分百完美的,优缺点必然同时存在,所以我们在使用设计模式的前应该充分分析优缺点,评定是否要使用该设计模式。

1.3设计模式的分类

面向对象设计原则及设计模式之创建型模式

1.4为什么要学习设计模式

1.站在巨人肩上。设计模式融合了许多前人的经验,并以一套标准的形式供开发人员使用
2.开发人员能简单地复用
3.设计模式使代码设计更加灵活,扩展性更高
4.许多构架源码,大量的使用了设计模式。学习之后有助于阅读源码

二.面向对象设计原则

2.1学习面向对象设计原则的目的

目的:确保编码的维护性和可复用性。

维护性:代码能被理解,改正,适应及扩展的难易程度
可复用性:代码可被重复利用的难易程度

2.2学习设计模式的基础

每一个设计都符合一个或者多个面向对象设计原则,同时面向对象设计原则也是设计模式评定的指标之一

2.3设计原则总览

面向对象设计原则及设计模式之创建型模式

2.3.1 单一职责原则

定义:一个对象应该只包含单一的职责,并且该职责被完整地封装到一个类中。

这让我想起了以前的男耕女织,你不能站我耕了田,回家还让我织布,是吧。这是虐待。
这是最容易理解的一个原则,同时也是一个最难界定的。完整?怎么才算完整,是不是耕田的除了会用锄头还得会用牛,当我有很多花式使用锄头和牛方式时,是不是用锄头和用牛就要分为两个类了呢?

在我们的系统中,大到模块,小到方法,承担的职责越多,复用的可能性就越小。所以我们经常会在做数据校验时,把校验维度放到最小,再做聚合。

2.3.2开闭原则

定义:软件实体应当对扩展开放,对修改关闭

意思是我们在做代码设计时,应该尽量在不修改原有代码的情况下进行扩展

一般为了满足开闭原则,都会使用接口,抽象类,但这些接口或者抽象类必须是相对稳定的。不然想象一下在接口添加一个方法,而接口有多个实现类。实在想象不到有什么是100满足开闭原则的

2.3.3里氏代换原则

定义:所有引用基类的地方必须能透明地使用其子类的对象。

这里说的是要多使用多态,引用基类的地方,传入子类并不会报错。所以在定义方法等时候,用基类,在使用的时候传入子类

那么问题来了,我在定义字段,方法入参的时候不管三七二十一,都用Object行不行?怕是会被打死

2.3.4依赖倒转原则

定义:高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖抽象

这说了跟没说一样,其实就是要求要针对接口编程,不应该针对实现编程。在实现依赖倒转原则时需要针对抽象层编程,而将具体类的对象通过依赖注入的方式注入到其他对象中。依赖注入是指当一个对象要与其他对象发生依赖关系时采用抽象的形式来注入所依赖的对象。这就是我们的service写的接口而spring给我们注入的是代理类

2.3.5接口隔离原则

定义:客户端不应该依赖那些它不需要的接口

个人的理解,其实与单一职责差不多,只不过他描述的是接口,并可提供定制服务。

2.3.6合成复用原则

定义:优先使用对象组合,而不是通过继承来达到利用的目的

就是要处理好is_a,和has_a关系,当是is_a的时候就用继承,has_a就用字段,也就是它的一个属性

2.3.7迪米特法则

定义:每一个软件单位对其它单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位

也就是实体应该尽量少地与其他实体发行相互作用,那么修改这一个实体的就会少影响其它的实体。这样就降低了耦合

三、创建型模式

3.1 简单工厂模式

简单工厂模式并不是四人帮定义的23种经典设计模式中的一种,但它可以作为学习其它工厂的基础

简单工厂的结构

1.Factory(工厂角色) 负责生产对象

2.Product(抽象产品角色) 作为其它产品的父类,同时是工厂方法的返回值

3.concreteProduct(具体产品角色) 工厂最终生产的对象

那些定义一个fruit(水果)父类的例子就不给大家举例了。给大家一个有点知识的:

大家都吃过无子西瓜,其实它就是三倍体的西瓜,不孕不育,所以无子。三倍体是二倍体与四倍体结合而成。而四倍体又是二倍体在细胞分裂生成生殖细胞的时候,滴上秋水仙素,抑制纺锤体分裂。这样它的花粉就是双倍体。传粉给其他做过同样的西瓜就生成了四倍体:

工厂:

/**
 * 工厂
 * @Auther: 杨秋颐
 * @since: 2018/12/28 20:39
 */
public class WaterMelonFactory {
    public static WaterMelon getProduct(Integer chromosome){
        if(chromosome.equals(2)){
            return new WaterMelon2();
        }else if(chromosome.equals(3)){
            return new WaterMelon3();
        }else if(chromosome.equals(4)){
            return new WaterMelon4();
        }
        return null;
    }
}


抽象产品角色:
/**
 * 抽象西瓜类
 * @Auther: 杨秋颐
 * @since: 2018/12/28 20:40
 */
public abstract class WaterMelon {

    //染色体
    public Integer chromosome;

    /**
     *
     * 功能描述: 生西瓜
     *
     * @param:
     * @return:
     * @auther: 杨秋颐
     * @date: 2018/12/28 20:45
     */
    public abstract WaterMelon breed(WaterMelon waterMelon);
}

具体产品角色

/**
 * 二倍体
 * @Auther: 杨秋颐
 * @since: 2018/12/28 20:43
 */
public class WaterMelon2 extends WaterMelon {
    //染色体
    public Integer chromosome = 2;

    @Override
    public WaterMelon breed(WaterMelon waterMelon) {
        int i = this.chromosome + waterMelon.chromosome;
        if(i%2>0){
            //3倍体 不育
            return null;
        }
        return WaterMelonFactory.getProduct(i/2);
    }
}


/**
 * 三倍体(无子西瓜)
 * @Auther: 杨秋颐
 * @since: 2018/12/28 20:43
 */
public class WaterMelon3  extends WaterMelon {

    //染色体
    public Integer chromosome = 3;

    @Override
    public WaterMelon breed(WaterMelon waterMelon) {
        //传入几倍体的花粉也没用,这家伙不育
        return null;
    }
}


/**
 * 4倍体
 * @Auther: 杨秋颐
 * @since: 2018/12/28 20:43
 */
public class WaterMelon4 extends WaterMelon{

    //染色体
    public Integer chromosome = 4;

    @Override
    public WaterMelon breed(WaterMelon waterMelon) {
        int i = this.chromosome + waterMelon.chromosome;
        if(i%2>0){
            //3倍体 不育
            return null;
        }
        return WaterMelonFactory.getProduct(i/2);
    }
}