某年某月的某一天公司业务扩大,接了个大项目。好了,产品经理又来了,哈哈哈哈,他说想要你实现一个界面展示界面,每次下拉刷新页面,然后你就开始埋头码代码,先是搞了一个叫请求的接口来管理网络请求和刷新界面,然后在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