如何利用面向对象的五大原则,将简单的代码复杂化

        某年某月的某一天公司业务扩大,接了个大项目。好了,产品经理又来了,哈哈哈哈,他说想要你实现一个界面展示界面,每次下拉刷新页面,然后你就开始埋头码代码,先是搞了一个叫请求的接口来管理网络请求和刷新界面,然后在model实现这个接口,三下五除二就把这个简单的功能给KO掉。心情美美的下班了。

public interface Request {

    void requestResource();

    void requestFailed();

    void requestSuccess();

    void updateView();

}

public class RequestModel implements Request{

    @Override

    public void requestResource() {

    //downLoad Resource

    }

@Override

public void requestFailed() {

}

    @Override

    public void requestSuccess() {

    updateView();

    }

    @Override

    public void updateView() {

    //update view

    }

}

        第二天上班,烦人的产品经理又来了,他说现在有一个新需求,用户可以下载自己喜欢的相关资源,图片文字等资源用户可以直接下载到本地,但是下载好的录音类资源只能在app中查看。你心想网络请求接口已经写好,那简单,开始继续埋头码代码,然后你发现,现在只需要下载功能就好了,不需要更新界面,这个时候去实现这个接口,会有一个多余的方法,不想出现这个方法就只能修改接口,网络请求在开发过程中跟很多功能都息息相关,所以你开始修改接口,幸好是项目刚开始,不然修改接口将是灾难性的,你开始思考怎么改,发现更新界面是一个职能,另外三个是一个职能,所以将现在的接口拆分成两个不同职能的接口,拆分完的两个接口,每个只负责自己相关的职能(单一职责原则【应该有且仅有一个原因引起类的变更,也就是接口或类和职责的关系是一一对应的】接口隔离原则【使用多个专门的接口比使用单一的总接口要好】

public interface Request {

    void requestResource();

    void requestFailed();

    void requestSuccess();

}

public interface View {

    void updateView();

}

原来实现一个接口改为实现两个接口

public class RequestModel implements Request,View{ ... }

//用户下载资源

public class DownLoadModel implements NetRequest {

    @Override

    public void requestResource() {

    //下载资源

    }

    @Override

    public void requestFailed() { }

    @Override

    public void requestSuccess(){}

}

        下载的功能实现后,我们来啃掉文件存储的骨头,图片和文字资源要给用户拿得到,所以要放在外部储存,语音资源要放在内部存储

public class SDStorage {

    void save(Object media){

    //文件储存在内存卡

    }

}

public class InternalStrorage{

    void save(Object media){

    //文件储存在数据库

    }

}

public class StroageManager {

    public static final int STROAGE_INTERNAL= 1;   

    public static final int STROAGE_SD = 2;

    public Object media;

    private int type = 1; 

    public StroageManager(Object media) {

        this.media = media;

    }

    public void setStroageType(int type) {

        this.type = type;

    }

    public void setMedia(Object media) {

        this.media = media;

    }

    public void save() {

        switch (type) {

            case STROAGE_INTERNAL:

                new InternalStrorage().save(media);

                break;

            case STROAGE_SD:

                new SDStorage().save(media);

                break;

        }

    }

}

        产品经理觉得两种方式不够,还应该增加数据库储存的方式,然后你就在心里骂他,fuck,就不能一次性把需求讲完么,你知道骂完也无济于事,好吧,接着搞,不就是搞多一个类嘛。那还不是小Case。又搞了个SQL数据库的。

public class SqlStorage {

    void save(Object media){

        //文件储存在数据库

    }

}

然后增加了标志位,和修改了save方法

public class StroageManager {

    ...

    public static final int STROAGE_SQL = 3;

    ...

     public void save() {

         switch (type) {

            case STROAGE_SD:

                new SDStorage().save(media);

                break;

            case STROAGE_INTERNAL:

                new InternalStrorage().save(media);

                break;

              case STROAGE_SQL: 

               new SqlStorage().save(media);

                break;

        }

    }

}

        改完后,你感觉增加一种存储方式,修改很麻烦,要改这么多地方,要是以后还有其他五花八门的储存方式,那岂不是要一直改改改,除了增加功能类外,还要增加n多个标志位,n多个switch case,反正大家都是调用同一个save方法。能不能搞一搞其他方式,增加新功能只需要增加功能类就可以呢。好了开试,首先大家方法都一样,那肯定是提取出来嘛。那我们先搞个接口。因为以后其他功能说不定也有保存的功能需要实现这个接口,调用其他保存方式,可能是保存在内存什么的,就叫资源存储吧。

public interface ResourceStroage {

    void save(Object media);

}

然后,刚刚的三个类就去实现这个借口。

public class SqlStorage implements ResourceStroage { ... }

public class SDStorage implements ResourceStroage { ... }

public class InternalStrorage implements ResourceStroage { ... }

接着修改StroageManager

public class StroageManager {

    private ResourceStroage resourceStroage;

    public StroageManager(ResourceStroage resourceStroage){//依赖注入

        this.resourceStroage = resourceStroage;

    }

    public void setResourceStroage(ResourceStroage resourceStroage) {

        this.resourceStroage = resourceStroage;

    }

    public void save(Object media) {

        resourceStroage.save(media);

    }

}

//调用的方式

new StroageManager(new SqlStorage()).save(media);

new StroageManager(new SDStorage()).save(media);

new StroageManager(new InternalStrorage()).save(media);

假设又有了一个新的存储方式,叫OtherStroage

public class OtherStroage implements ResourceStroage { ... }

//调用

new StroageManager(new OtherStroage()).save(media);

这样子,以后产品经理又要增加新的存储方式,只需要直接拓展就可以了。前面的实现方式,一旦有新增的方式,就要改动原先StroageManager代码,这样设计是很不合理的。相比较之下,改动后的实现方式就显的优雅多了,只关心拓展。(开闭原则一个软件实体如类、模块和函数应该对扩展开放,对修改关闭】、 依赖倒置【A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。 B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。以及 里氏替换原则【在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。】

跟产品经理的梁子到这里就先告一段落啦,有时间会说到如何写出完全反五大原则,将代码简单化 哈哈哈

附上一部分资料:

https://www.jianshu.com/p/a7dc2e007d76

https://www.cnblogs.com/cody1988/archive/2012/06/05/2536254.html

https://blog.csdn.net/wzlyd1/article/details/50970635

http://www.supmen.com/r1qmn0dvzy.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。