设计模式(总起式:六大原则)

完整代码见github:https://github.com/BenMario315/design-patterns

常用的设计模式可概括为23中,根据其特点可分类为三大类型:
    一、创建型:1.单例模式,2.工厂方法模式,3.抽象工厂模式,4.建造者模式,5.原型模式
    二、结构型:6.代理模式,7.装饰模式,8.适配器模式,9.组合模式,10.桥梁模式,11.外观模式,12.享元模式
    三、行为型:13.模板方法模式,14.命令模式,15.责任链模式,16.策略模式,17.迭代器模式,18.中介者模式,19.观察者模式,20.备忘录模式,21.访问者模式,22.状态模式,23.解释器模式

不可绕过的六大原则:
    一、单一职责原则:一个类应该只有一个引起它变化的原因,即一个类应该只有一个职责。【举例见下文】
    二、里氏替换原则:如果对于一个类型S的对象o1,都有一个类型为S的对象o2,使得以S定义的所有程序P在所有对象o1都能转换成o2时,程序P的行为没有发生变化,那么类型T是类型S的子类。(或者也可以这样说:所有使用基类的地方都能透明的使用其子类对象。)【举例见下文】
    三、依赖倒置原则:高层模块不应该依赖底层模块,两者都应该依赖抽象;抽象不应依赖细节,细节应该依赖抽象。(抽象是指抽象类或者接口;细节是指实现类)【举例见下文】
    四、接口隔离原则:客户端不应该依赖它不需要的借口。(或者这一这样说:类的依赖应该建立在最小的接口上。)【举例见下文】
    五、迪米特法则:一个对象应该对其他对象尽可能的少了解。【举例见下文】
    六、开闭原则:一个软件应该适当的对扩展开放,对修改关闭。意思是,在设计一个模块是应该可以在不被修改的情况下被扩展,即一个模块在不被修改源代码的情况下可以修改其行为。【举例见下文】
    总结:其中开闭原则是总的指导原则,其他(单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则)都是开闭原则的具体形态。

    可能这样只是一些概念型的文字,对我们程序员来说,最直观的还是代码+注释,下面分别举例
    一、单一职责原则

/**
 * 我们通常都是有这样一个类,这个类的职责就是定义该类型有的属性。
 */
public class User {
    
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
职责类一
/**
 * 一般我们还会有这样一个类,它的职责是对User对象做一定的业务逻辑操作
 */
public class UserServiceImpl {
    public void addUser(User user) {
        //...
    }

    public void delUser(String name) {
        //...
    }

    public void findUser(String name) {
        //...
    }

    public void modifyUser(User user) {
        //...
    }
}
职责类二

随便举个例子说明问题就行了。意思就是我们一般都会在一个类中集中做一种事情,赋予这个类一个职责。所以这可能也是比较混乱的,因为每个人的划分标准可能以相同,所以也别太较真抬杠,所以如果你非要把所有的模型model的添加放在一个service类中,也算符合吧,但小心别人用枪打你。一般我们还是需要和团队形成一样的形式。我们身子弱,为了安全起见。
    二、里氏替换原则

public class Main {

    public static void main(String[] args) {
        Alimal alimal_1 = new Dog();
        Alimal alimal_2 = new Cat();

        alimal_1.call();
        alimal_2.call();
    }

}

abstract class Alimal {
    abstract void call();
}

class Dog extends Alimal {

    @Override
    public void call() {
        System.out.println("汪汪汪。。。");
    }
}

class Cat extends Alimal {

    @Override
    public void call() {
        System.out.println("喵喵喵。。。");
    }
}
View Code

其实本来这里的四个类,应该分别在四个文件中,但是为了方便看代码我将它们放在了一起,你会发现,这就是多态里面的性质,什么时候是向上造型,什么时候是向下造型。但这里的里氏替换原则很简单一点,就是凡是父类对象能出现的地方,用子类都可以替换。你会发现里氏替换的第一个定义其实是定义了什么是子类。
    三、依赖倒置原则

public interface Car {
    void move();
}

class A implements Car{

    @Override
    public void move() {
        System.out.println("开着A车去兜风。。。");
    }
}

class B implements Car{

    @Override
    public void move() {
        System.out.println("开着B车去兜风。。。");
    }
}
第一部分
public interface Drive {
     void drive(Car car);
}

class SpecialityDrive implements Drive{

    @Override
    public void drive(Car car) {
        System.out.println("我是专业的司机");
        car.move();

    }
}

class AmateurDrive implements Drive{

    @Override
    public void drive(Car car) {
        System.out.println("我是业余的司机");
        car.move();
    }
}
第二部分

本来还有第三部分,我不想写了,猜也猜到三部分是调用了。重点不在调用而在第二部分,熟悉概念的应该看出来了,都是使用的接口,高层模块不应该依赖底层模块,而是都应该依赖抽象。虽然这里面也体现了一些设计模式,但后面会详细说明,这里不是重点,故略过。
    四、接口隔离原则

这部分不写代码里吧,感觉如果我把代码写在一起反而不好说明,而且这个应该用文字更好说明。一般一个系统平台都至少有两部分(用户使用的部分:门户,管理人员使用的部分:后台管理),肯定存在一些对象模型,门户的权限不足,比如说商品,门户肯定不允许修改商品的,他们只有显示的,所以我门在定义商品操作接口的时候需要分开定义,虽然看上去是同一个,但这样能避免我们去实现没必要的方法。同时也为了代码更安全。
    五、迪米特法则

public class tets {
    public static void main(String[] args) {
        Someone one  = new Someone();
        one.call(new Friend());
    }
}

class Someone{
    public void call(Friend friend){
        friend.forward();
    }
}

class Friend {

    private Stranger stranger = new Stranger();
    public void forward(){
        stranger.strangerMothed();
    }
    public void friendMothed(){
        System.out.println("这里是朋友的方法");
    }

}

class Stranger{
    public void strangerMothed(){
        System.out.println("这里是陌生人的方法");
    }
}
View Code

正常来说肯定也是不在同一个文件的,这一段可能很多人明不能直接的看明白,我整理一下迪米特法则的解释描述:“只跟直接的朋友进行沟通”;“不跟陌生人说话”;“对其他的单位了解的越少越好”。知道这三条解释以后,我们再来理解以上的代码,someone 想要调用一个他不认识的人的方法,他不直接去调用,而是通过friend这样一个中间人去调用,如比说相约一个在餐厅遇到的漂亮女孩,我们不要直接过去,而是通过朋友,或许朋友也不直接认识他,那就再找朋友的朋友,以此类推,就以一个熟人的身份约到了她。类似的做法,我们通常的结构可能是Controller->Service->Mapper,在这个结构中,我们都是将Service对象注入Controller中,将Mapper对象注入Service中,尽可能不要让Controller和Mapper直接接触,因为控制层和操作层中间还有业务层,控制层和操作层是陌生人的关系。
    六、开闭原则

public interface Book {

    String getName();
    BigDecimal getPrice();

}

class NovelBook implements Book{

    public NovelBook(String name,BigDecimal price){
        this.name = name;
        this.price = price;
    }

    private String name;
    private BigDecimal price;

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public BigDecimal getPrice() {
        return price;
    }
}

class OldNovelBook extends NovelBook{

    public OldNovelBook(String name, BigDecimal price) {
        super(name, price);
    }

    public BigDecimal getPrice(){
        return super.getPrice().multiply(new BigDecimal("0.7"));
    }
}
View Code

同样的,调用的部分我没有写,但应该已经想到明显,调用的时候统一使用接口Book,但我们传入的时候,可能传入的是NovelBook,也可能传入的是OldNovelBook,旧的小说的价格会打七折,所以我们不需要修改什么代码,只要按照一定的日期区分新旧数据,船舰对象的时候区分出来,价格就自然会打折,这就是开闭原则,在尽可能不修改代码的情况下使用扩展性改变原有的行为。

  总结
其实这只是个引子,但其实也还算重要,这是一些设计模式主要思想框架。有些我们可能只是不知道名称,但平时就在使用,但不知道名称有一个很尴尬的就是无法和别人交流,或者你觉得什么东西很好,但是你不知道怎么表达。斯坦福大学的一个教授说过一句话,“当一个事物出现时,我们总是先赋予它一个名称,其实并不是一定要给他一个名字,只是为了赋予我们使用它的能力”。

原文地址:https://www.cnblogs.com/ben-mario/p/10641516.html