设计模式一

187 阅读7分钟

这是我参与更文挑战的第13天,活动详情查看: 更文挑战

设计模式

使用设计模式,可以让我们的代码具有更好的可读性、可扩展性、可读性、重用性、符合高内聚低耦合的特点。作为程序员,是我们经常听到的概念,也是我们程序员必须深入学习,了解的知识。

设计模式种类

该表和图来源于菜鸟教程

序号模式 & 描述包括
1创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern)
2结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern)
3行为型模式 这些设计模式特别关注对象之间的通信。责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern)
4J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。MVC 模式(MVC Pattern) 业务代表模式(Business Delegate Pattern) 组合实体模式(Composite Entity Pattern) 数据访问对象模式(Data Access Object Pattern) 前端控制器模式(Front Controller Pattern) 拦截过滤器模式(Intercepting Filter Pattern) 服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern)

下面用一个图片来整体描述一下设计模式之间的关系:

设计模式之间的关系

七大原则

1、单一职责原则(Single Responsibility Principle)

模块的组成元素之间的功能相关性。一个类只负责一项职责。

2、里氏代换原则(Liskov Substitution Principle)

面向对象设计的基本原则之一。任何基类可以出现的地方,子类一定可以出现,而派生类也能够在基类的基础上增加新的行为。鸡肋设定好了规范,虽然不强制要求子类必须遵循这些契约,但是也不可任意修改,会降低程序的移植性,增加程序的耦合性(基类的修改会影响到所有子类)

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,依赖于抽象而不依赖于具体,核心思想面向接口编程。

4、接口隔离原则(Interface Segregation Principle)

可以增加接口数,来保证一个类依赖于另一个类的最小接口,要为各个类建立它们需要的专用接口。比如B依赖于A的1、2接口,C依赖于A的3、4接口,就可以把1、2接口隔离出来作为新的接口。

5、迪米特法则,又称最少知道原则(Demeter Principle)

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

6、合成复用原则(Composite Reuse Principle)

尽量使用合成/聚合的方式,而不是使用继承。

7、开闭原则(Open Close Principle)

对扩展开放,对修改关闭。通过使用接口和抽象类来实现在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。

单一职责原则

单一职责原则表示:模块的组成元素之间的功能相关性。一个类只负责一项职责。

起初,业务只有一种类型的动物,食草类动物,我们的代码这样写的。

package com.wangscaler.singleresponsibility;

/**
 * @author wangscaler
 * @date 2021.06.16 10:25
 */
public class SingleResponsibilityPrinciple {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.eat("牛");
        animal.eat("羊");
        animal.eat("马");
    }
}

class Animal {
    public void eat(String type) {
        System.out.println(type + "吃草");
    }
}

这完全没问题的,随着业务的增长,发现我们增加了食肉类动物,如果我们直接写animal.eat("老虎");这就违反了我们单一职责的原则,显然是不合适的。那么我们的代码就需要重写。

正确案例一如下:

package com.wangscaler.singleresponsibility;

/**
 * @author wangscaler
 * @date 2021.06.16 10:25
 */
public class SingleResponsibilityPrinciple {
    public static void main(String[] args) {
        Carnivorous carnivorous = new Carnivorous();
        Herbivorous herbivorous = new Herbivorous();
        herbivorous.eat("牛");
        herbivorous.eat("羊");
        herbivorous.eat("马");
        carnivorous.eat("老虎");
        carnivorous.eat("狮子");
    }
}
class Herbivorous {
    public void eat(String type) {
        System.out.println(type + "吃草");
    }
}

class Carnivorous {
    public void eat(String type) {
        System.out.println(type + "吃肉");
    }
}

这样改,显然是符合单一职责原则的,在类下有很多方法时,可以采取使用。在这里方法较少,这样写的话,开销比较大,所以可以违法类的单一职责原则,让方法保持单一职责原则。

正确案例二:

package com.wangscaler.singleresponsibility;

/**
 * @author wangscaler
 * @date 2021.06.16 10:25
 */
public class SingleResponsibilityPrinciple2 {
    public static void main(String[] args) {
        Animals animals = new Animals();
        animals.herbivorousEat("牛");
        animals.carnivorousEat("老虎");
    }
}

class Animals {
    public void herbivorousEat(String type) {
        System.out.println(type + "吃草");
    }

    public void carnivorousEat(String type) {
        System.out.println(type + "吃肉");
    }
}

采用这种写法的话,当我们增加种类,只需要添加新的种类的方法,而之前的代码是不需要动的。比如此时我们增加既吃肉又吃草的动物。只需要这样修改。

package com.wangscaler.singleresponsibility;

/**
 * @author wangscaler
 * @date 2021.06.16 10:25
 */
public class SingleResponsibilityPrinciple2 {
    public static void main(String[] args) {
        Animals animals = new Animals();
        animals.herbivorousEat("牛");
        animals.carnivorousEat("老虎");
        //不要计较熊猫吃不吃肉和草,杠精绕路。
        animals.eat("熊猫");
    }
}

class Animals {
    public void herbivorousEat(String type) {
        System.out.println(type + "吃草");
    }

    public void carnivorousEat(String type) {
        System.out.println(type + "吃肉");
    }

    public void eat(String type) {
        System.out.println(type + "既吃肉还吃草");
    }
}

我们可以看到,现在只需要增加新业务的代码,之前写的代码是没有影响的。

**总结:**单一职责原则的关键在于将业务中类的不同职责分离划分到不同的类或者接口。原则就是一个方法尽可能的处理一个职责,当然职责间肯定是有关联的,这就需要根据业务和需求来划分隔离了。如果方法较多,最好是在类上隔离,如果方法少且逻辑简单的情况下,可以让类违背单一职责原则,让方法保持该原则。

接口隔离原则

可以增加接口数,来保证一个类依赖于另一个类的最小接口,要为各个类建立它们需要的专用接口,接口应该尽量细化,一个接口对应一个功能模块,同时接口里面的方法应该尽可能的少,使接口更加轻便灵活。比如B依赖于A的1、2接口,C依赖于A的3、4接口,就可以把1、2接口隔离出来作为新的接口;3、4隔离出来作为新的接口。

错误示例:

起初我们的接口是写了所有的行为,然而马只依赖于eat、run;鸭子依赖于eat、run、swim;天鹅依赖于所有的方法。对于马来说fly方法显然是没用的方法也得实现,这样将不符合逻辑。

package com.wangscaler.interfacesegregation;

/**
 * @author wangscaler
 * @date 2021.06.16 16:35
 */
public class InterfaceSegregationPrinciple {
    public static void main(String[] args) {
        IAction horse = new Horse();
        horse.eat();


    }

    interface IAction {
        void eat();

        void fly();

        void run();

        void swim();
    }

    static class Horse implements IAction {

        public void eat() {
            System.out.println("马会吃");
        }

        public void fly() {

        }

        public void run() {
            System.out.println("马会走");
        }

        public void swim() {

        }
    }

    class Duck implements IAction {

        public void eat() {
            System.out.println("鸭子会吃");
        }

        public void fly() {

        }

        public void run() {
            System.out.println("鸭子会走");
        }

        public void swim() {
            System.out.println("鸭子会游泳");
        }
    }

    class swan implements IAction {

        public void eat() {
            System.out.println("天鹅会吃");
        }

        public void fly() {
            System.out.println("天鹅会飞");
        }

        public void run() {
            System.out.println("天鹅会走");
        }

        public void swim() {
            System.out.println("天鹅会游泳");
        }
    }
}

我们发现所有的动物均有eat、run的行为,而fly和swim是特有的行为,所以将接口按照接口隔离原则改变后如下:

正确示例。

package com.wangscaler.interfacesegregation;

/**
 * @author wangscaler
 * @date 2021.06.16 16:35
 */
public class InterfaceSegregationPrinciple1 {
    public static void main(String[] args) {
        Horse horse = new Horse();
        horse.eat();
        horse.run();
    }

    interface IEatAndRunAction {
        void eat();

        void run();
    }

    interface IFlyAction {
        void fly();
    }

    interface ISwimAction {
        void swim();
    }

    static class Horse implements IEatAndRunAction {

        public void eat() {
            System.out.println("马会吃");
        }

        public void run() {
            System.out.println("马会走");
        }


    }

    class Duck implements IEatAndRunAction, ISwimAction {

        public void eat() {
            System.out.println("鸭子会吃");
        }

        public void run() {
            System.out.println("鸭子会走");
        }

        public void swim() {
            System.out.println("鸭子会游泳");
        }
    }

    class swan implements IEatAndRunAction, ISwimAction, IFlyAction {

        public void eat() {
            System.out.println("天鹅会吃");
        }

        public void fly() {
            System.out.println("天鹅会飞");
        }

        public void run() {
            System.out.println("天鹅会走");
        }

        public void swim() {
            System.out.println("天鹅会游泳");
        }
    }
}

**总结:**接口的粒度一定要合理,太小就会导致接口增多,太大会导致灵活性降低,减少代码的冗余,提高系统的内聚性。