设计模式系列(总纲)

前言

关于设计模式,想必大家的第一感觉就是过于高深,有点虚吧。相对来说,我们还是更熟悉ssh或者ssm之类的开发框架,一个工具用久了就会熟能生巧,就像刷漆工,时间长了也知道如何刷的一手漂亮的好墙。我们把springmvc用的很溜,把mybatis用的很溜,但这只是一个合格的程序员应该具备的能力之一。于是我们便成为了代码工人,想站在更高角度去设计,设计模式是必不可少的。

简介

废话那么多,我们直接进入正题呢,到底什么是设计模式呢?我在设计模式之禅上摘抄出这一句话:

设计模式(Designpattern)是一套理论,由软件界的先辈们总结出的一套可以反复使用的经验,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。

为什么要学习设计模式

因为要装逼啊…咳咳,大家请忽略前面那句话。相信大家都有阅读spring源码的经历吧,看到类层次设计图,为什么会有这么多的接口,这么多的抽象类,这么多实现,心里有一万只草泥马狂奔而过。spring处处都是设计模式的体现,所以若想攻下spring,设计模式是必学的。其次,作为一个coder,最烦的就是需求变更,我们可以分析现有的需求,预测可能发生的变更,但是我们不能控制需求的变更。既然需求不可变更,那如何拥抱变化呢?设计模式给了我们知道,提出了6大原则,在此基础上提出了23种设计模式。如果你通晓了23种设计模式,那你就可以站在一个更高的层次去赏析程序代码,完成从代码工人到架构师的转变~>~.

设计模式六大原则

单一职责原则(最容易理解,最难实施的一个原则)

描述:应该有且仅有一个原因引起类的变更

相信大家已经很不屑了,切,这么简单的东西还拿出来说。可是道理简单实现难啊。下面我给大家示例一些代码:
    public interface IUserInfo {
    
        void setUserName(String name);
    
        void setUserAge(int age);
    
        void walk();
    
        void seeMovie();
        
        void addRole(Role role);
    }
    
相信初级程序员都不会犯这样的错,将用户信息和用户行为放在同一个类里。最佳的做法就是将用户信息和用户行为分开管理,加角色动作也应该放在相应的实现类里。这是我临时想的例子,虽然很简单,但足以表达单一原则的意思。单一职责原则最难划分的就是职责,职责没有量化的标准,职责划分的过于细,类的数量会暴增,系统复杂度会以指数趋势增长,所以建议是接口一定要做到单一职责,累类设计尽量做到单一职责。

里氏替换原则(使用时一定要注意子类的个性化..)

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

java与c++不同,java是一门单根继承的语言。使用extend关键字声明继承,若不使用,则代表继承Object。子类继承父类,会拥有父类的方法和属性,很多开源框架的扩展接口都是通过继承父类来完成的。通过这个特性,我们明白只要父类存在的地方,我们都可以替换成子类,并且编译器不会报错。但是java允许子类重写父类方法,这种子类的个性化有时会严重违背里氏替换原则.给大家上个示例:
    public class AnyClazz{
        //有某一个方法,使用了一个父类类型
        public void someoneMethod(Parent parent){
            parent.method();
        }
    }
    
    public class Parent {
    
        public void method(){
            System.out.println("parent method");
        }
    }
    
    public class SubClass extends Parent{
    
        //结果某一个子类重写了父类的方法,说不支持该操作了
        public void method() {
            throw new Exception();
        }
        
    }
    
    public class Test{
    
        public static void main(String[] args) {
            AnyClazz anyClazz = new AnyClazz();
            anyClazz.someoneMethod(new Parent());
            anyClazz.someoneMethod(new SubClass());
        }
    }

程序运行结果肯定会报错,这就相当于我们埋了一个雷,子类复写父类方法,编译期不报错,运行期就会报错。所以建议在项目中使用此原则时,应尽量避免子类的个性,一旦子类有个性,子类和父类的矛盾将可能直接破坏整个程序。

依赖倒置原则(很常见,现在的企业开发中业务层都有对应接口)

描述:面向接口编程。

在Java中的语言表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类发生的。接口和抽象类不依赖于实现类。实现类依赖接口或抽象类。简而言之,就是之前的描述~~。给大家举个例子:

     public class Cooker {
        public void work(){
            System.out.println("我会做饭");
        }
    }
    public class Boss {
    
        public void recruitPerson (Cooker cooker){
            cooker.work();
        }
    }
    public class Test {
        
            public static void main(String[] args) {
                Boss boss = new Boss();
                boss.recruitPerson(new Cooker());
            }
        }
        运行结果:我会做饭
上面代码一个大老板刚开了一个超市,在招厨师。假如我们要是招大堂经理怎么办?创建经理类,在boss中增加招经理的方法同时在test中修改。这样改好吗?仅仅是需求改了一点点,我们就要在所有代码里进行一些改动,这代价也太大了吧,耦合性忒高了~
不要着急,依赖倒置原则可以帮我们解决这个问题,它可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码可读性和维护性。修改之后的例子如下:
public interface IWorker {

    void work();
}

public class Cooker implements IWorker {
    @Override
    public void work(){
        System.out.println("我会做饭");
    }
}

public class Manager implements IWorker {
    @Override
    public void work() {
        System.out.println("糊弄老板,指挥其他人干活");
    }
}

public interface IBoss {
    void recruitPerson(IWorker iWorker);
}

public class Boss implements IBoss{

    @Override
    public void recruitPerson(IWorker iWorker) {
        iWorker.work();
    }
}

public class Test {

    public static void main(String[] args) {
        Boss boss = new Boss();
        IWorker iWorker = new Manager();
        boss.recruitPerson(iWorker);
    }
}
运行结果:
糊弄老板,指挥其他人干活
咋一看,类变得更多了..使用技术总是要有点成本的吗,但假如以后我们在增加招理货员,招收银员等等改动就小了。其实这个原则的本质就是通过抽象是各个类和模块的实现独立,不互相影响,实现松耦合。


接口隔离原则(六大原则中身材最好的那个小姐姐)

描述:接口尽量细化,同时接口中方法尽量少。

看到上面的描述可能会有点疑惑,这个和第一个的小姐姐(单一职责原则)有点像啊。单一职责衡量的是类的和接口职责要单一,对,是职责!而接口隔离原则就只是要求接口的粒度尽可能的小,最好就是一个接口一个方法(这是完美符合接口隔离原则的),不过这样做的话是明显要被其他小伙伴干死的(2333~~~)。接口粒度太小,接口数就会暴增,开发人员呛死在接口的海洋里,接口粒度太大,灵活性降低,无法提供定制服务。给大家举个例子:
//美食
public interface ICate {

    void goodLool();
    void niceTaste();
    void cleanFood();
}

public class Cate implements ICate{

    @Override
    public void goodLool() {
        System.out.println("色相看起来很好O(∩_∩)O哈哈~");
    }

    @Override
    public void niceTaste() {
        System.out.println("吃起来,味绝了(*^▽^*)");
    }

    @Override
    public void cleanFood() {
        System.out.println("食材很干净,食用放心");
    }
}

public abstract class AbstractPerson {

    protected ICate iCate;

    public AbstractPerson(ICate iCate) {
        this.iCate = iCate;
    }

    abstract void eat();
}

public class Person extends AbstractPerson {
    public Person(ICate iCate) {
        super(iCate);
    }

    @Override
    void eat() {
        iCate.cleanFood();
        iCate.goodLool();
        iCate.niceTaste();
    }
}

public class Test {

    public static void main(String[] args) {
        ICate iCate = new Cate();
        AbstractPerson abstractPerson = new Person(iCate);
        abstractPerson.eat();
    }
}

运行结果:
食材很干净,食用放心
色相看起来很好O(∩_∩)O哈哈~
吃起来,味绝了(*^▽^*)
    上面的例子我去吃美食,我对美食的标准是看起来,闻起来像,吃起来放心(吃货up!up!)。可是每个人对美食的标准都是一样的,你总不能因为臭豆腐闻起来很臭就不是美食吧。可是现在接口定义了只有三个条件都满足才能叫美食,这个该怎么办?难道要再扩展一个美食接口,只定义味道标准,可是我们的抽象类依赖的是ICate接口,我们的person又怎么只根据味道去分辨是不是美食呢?我们采用接口隔离原则去细化接口,示例如下:

public interface IGoodLookCate {

    void goodLook();
}

public interface IGoodTasteCate {


    void niceTaste();
    void cleanFood();
}

public class Cate implements IGoodLookCate,IGoodTasteCate{

    @Override
    public void goodLook() {
        System.out.println("色相看起来很好O(∩_∩)O哈哈~");
    }

    @Override
    public void niceTaste() {
        System.out.println("吃起来,味绝了(*^▽^*)");
    }

    @Override
    public void cleanFood() {
        System.out.println("食材很干净,食用放心");
    }
}
通过这样的重构后,不仅是要好吃的美食还是要好看的美食,都可以保持接口的稳定。当然你如果要说美食的标准又变了,接口还得改。没错,但是设计是有限度的,不能无限考虑未来,不然会陷入到过度设计的泥潭当中。

迪米特法则(六大原则中最害羞的小姐姐)

描述:一个对象尽量不要知道太多关于其他对象的事

这个原则的核心观念就是类间解耦,低耦合。通俗一点来讲就是1.只和朋友交流2.朋友之间也是有距离的。举个例子说明下吧:

public class Goods {
}

public class Worker {


    public void sendGoods(List<Goods> list){
        System.out.println("送货喽:"+list);
    }
}

public class Boss {
    private static final List<Goods> list = new ArrayList<Goods>();

    static {
        list.add(new Goods());
        list.add(new Goods());
        list.add(new Goods());
    }

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.sendGoods(list);
    }

}

工人送货的场景,老板和工人是直接关系,工人和货物是直接关系,但是代码中老板和货物也搅在了一起,出现了过多依赖,破坏了老板类的健壮性,已经违反了迪米特法则。正确做法应该是将货物的逻辑抽取到工人当中(因为非常简单,就不上代码示例了)。朋友之间也是有距离的应当如何理解呢?假如类A提供了getA(),getB(),getC()三个方法,类B在逻辑内对ABC三个方法进行调用或者根据其返回值决定逻辑,这样也违反了迪米特法则,正确做法应是将对ABC三个方法调用的逻辑抽取封装到A中,同时将ABC三个方法进行私有化。

开闭原则(六大原则中当之无愧的大姐大)

描述:对扩展开放,对修改关闭

开闭原则是六大原则里的精神领袖,也是最虚的原则。前面五个原则就是对开闭原则的具体解释。意思就是现在的系统中,不需要修改原有的代码,只需要扩展一些新的实现就可以搞定。但是没有任何一个系统可以做到,哪怕是spring也不可以,虽说他的扩展性已经强的变态。

以上6个原则是为了之后23种设计模式的描述,基本上都是我自己的理解,或许形容有好有差,不需要在意,各位完全可以有自己的理解。

下节预告:单例模式

    原文作者:设计模式
    原文地址: https://segmentfault.com/a/1190000012042349
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞