代码设计的核心思想

分层思想

将逻辑分层处理,不同层次之间有不同的职责,不存在跨层访问,从而降低耦合,职责清晰,容易拓展,容易复用。

分层思想就是宏观的单一职责原则。

每一层有自己的职责,也只负责自己的职责,每一层只使用上层的服务并向下层提供服务。

最简单的例子就是计算机网络的五层协议,每一层协议只对下层负责,不会出现跨层访问的情况。

那么,分层有啥好处呢?我们上代码。

假如我们要做一个语音直播间,在不分层的情况下,我们就把所有代码都梭哈到页面里面:

class PageVoiceLiveRoom {
    // 存储消息
    private List<String> msgs = new ArrayList();
    
    // 负责控制声音
    private AudioEngine audioEngine;
    // 负责控制消息
    private SocketEngine socketEngine;
    
    // 绘制UI
    void drawUI() {
    }
        
    // 打开麦克风
    void openMic() {
       audioEngine.openMic();
    }
    
    // 打开扬声器
    void openSpeaker() {   
        audioEngine.openSpeaker();
    }
    
    // 发消息
    void sendMsg(String msg) {
        socketEngine.sendMsg(msg);
    }
    
    // 收到消息
    void onReceiveMsg(String msg) {
        msgs.add(msg);
    }
}

写完感觉美滋滋,代码良好运行,一点问题都没有。

突然有一天,产品说要加个小窗功能,小窗的情况下页面不存在,但是声音和逻辑都在。

我代码都写在页面上了,你这页面都没了,声音肯定也没了。

那这得改啊,不能全干在 UI 上,得拆开,UI 只处理 UI,其他的放在非 UI 上才行,这样的话,一旦页面销毁,也只是 UI 相关的销毁,声音和逻辑也都在,改吧。

于是,就有了下面的代码。

首先定义非 UI 逻辑,用来保存数据。

class VoiceLiveRoomData {
    // 存储消息
    private List<String> msgs = new ArrayList();
    // 负责控制声音
    private AudioEngine audioEngine;
    // 负责控制消息
    private SocketEngine socketEngine;
    
    // 打开麦克风
    void openMic() {
       audioEngine.openMic();
    }
    
    // 打开扬声器
    void openSpeaker() {   
        audioEngine.openSpeaker();
    }
    
    // 发消息
    void sendMsg(String msg) {
        socketEngine.sendMsg(msg);
    }
    
    // 收到消息
    void onReceiveMsg(String msg) {
        msgs.add(msg);
        showMsg(msg);
    }

}

然后定义 UI 逻辑,不再存储数据,只是负责展示。

class PageVoiceLiveRoom {
    private VoiceLiveRoomData roomData;
    
    // 绘制UI
    void drawUI() {
    }
        
    // 打开麦克风
    void openMic() {
        roomData.openMic();
    }
    
    // 打开扬声器
    void openSpeaker() {   
        roomData.openSpeaker();
    }
    
    // 发消息
    void sendMsg(String msg) {
        roomData.sendMsg(msg);
    }
    
    // 收到消息
    void onReceiveMsg(String msg) {
        showMsg(msg);
    }
}

这样以来,UI 部分只负责展示数据和处理 UI 事件,不再存储数据。当 UI 销毁后,VoiceLiveRoomData会继续存在,并继续处理非 UI 部分的逻辑,比如存储消息。当 UI 再建立后,直接再次获取一次VoiceLiveRoomData即可,数据并没有缺失,声音也没有中断。此时PageVoiceLiveRoom就叫做 UI 层,VoiceLiveRoomData就叫做数据层,这也是 MVC 思想的核心。

我们可以给它起名叫做:数形分离思想,数是数据,形是图形,也就是 UI。

那么,代码这样就行了吗?

不行!这并不能完美体现分层思想的优点。

比如:现在我要做一个视频直播间,视频直播间包含了语音直播间的功能,怎么包含呢?

很简单啊,直接新建一个VideoLiveRoomData,让它继承VoiceLiveRoomData就行了,子类通过继承可以复用父类功能。

继承也是分层的一种体现。

但是,如果我想做一个没有声音没有视频、只能打字的聊天室,要怎么办呢?

这就不能继承了,因为它的功能太少了,它的功能是语音直播间的子集,所以,只能让语音直播间继承它。

所以,我们要改代码,我们的层级应该是下述这样的。

  • A:普通直播间,没有语音功能。

  • B:语音直播间,有语音功能,有 A 的所有功能,是 A 的下级,并且继承 A。

  • C:视频直播间,有视频功能,有 B 的所有功能,是 B 的下级,并且继承 B。

好,逻辑理清了,我们就上代码。

普通直播间定义:

// 普通直播间,只有打字功能
interface IBaseLiveRoomContext {
     void sendMsg(String msg);
}

class BaseLiveRoomContextImpl implements IBaseLiveRoomContext {
    private SocketEngine socketEngine;
    void sendMsg(String msg) {
        socketEngine.sendMsg(msg);
    }
}

语音直播间定义:

// 语音直播间,有普通直播间的所有功能,并且拓展了语音相关逻辑
interface IVoiceLiveRoomContext extends IBaseLiveRoomContext {
    void openMic();
    
    void closeMic();
    
    void openSpeaker();
    
    void closeSpeaker();
}

class VoiceLiveRoomContextImpl extends BaseLiveRoomContextImpl implements IBaseLiveRoomContext {
    private AudioEngine audioEngine;
    
    void openMic() {
        audioEngine.openMic();
    }
    
    void closeMic() {
        audioEngine.closeMic();
    }
    
    //.....
}

视频直播间定义:

// 视频直播间,有语音直播间所有功能,并且拓展了视频相关逻辑
interface IVideoLiveRoomContext extends IVoiceLiveRoomContext {    
    void startPreview();
    
    void stopPreview();
}

class VideoLiveRoomContext extends VoiceLiveRoomContextImpl implements IVideoLiveRoomContext {
    private VideoEngine videoEngine;
    
    // .......
}

我们看代码就知道,我们的层级关系为:

其中,下级可以使用上级的所有功能,这就是分层的另一个好处:复用。

这类似于树形结构,如果将来出了个新直播间 D,如果 D 具有视频直播间的所有功能,那么直接让 D 继承VideoLiveRoomContext即可,也就是放在VideoLiveRoomContext的下面;如果 D 没有视频直播间的所有功能呢,但是有语音直播间的所有功能,那么直接让它继承VoiceLiveRoomContextImpl即可。

其实,我们的分层架构,就像是一棵树,新增的功能就是树上的一个节点,总有插入这个节点的地方。

分层就是为了每一层能各司其职,互不影响,从而降低耦合,容易拓展。

粒度细化思想

将大功能拆成一个个的小功能,越小越好。

一个城市可以拆分成一个个的县,一个县又能拆成一个个的乡,一个乡又能拆成一个个的村,一个村又能拆成一个个的家庭,一个家庭又能拆成一个个的人,如果把城市比喻成沙漠,那么一个个的人就像一粒粒的沙子,这就是粒度细化。

这样有啥好处呢?

比如,我有个模块 A,包含了功能 B1 和 B2,假如,我只想使用 B1 功能,如果模块 A 没有拆分的话,我必须依赖模块 A,这样就可以使用 A 里面的 B1 了,但是这样导致我也依赖了 B2 了,如果将来 B2 有改动,我就不得不跟着变化,这明显是不好的,违背了最少知识原则,也不满足开放闭合原则。

所以,我们要对模块 A 进行拆分,拆分成 B1 和 B2 两个部分,这样,我只需要依赖 B1 即可,B2 的任何改动都不会对我造成任何影响。

所以,你看,粒度细化是不是宏观的接口隔离原则?接口隔离原则要求接口尽量小,而粒度细化要求模块尽量小。

那么,粒度是不是越细越好呢?

不是!

比如,一个人的基本功能是吃喝拉撒,你把这四个功能定义给一个人就行,而不需要把每一个功能都单独定义,因为这样就不符合现实了。

所以,粒度的大小要符合常规逻辑。

而且我们会发现,粒度细化思想也是宏观的最少知识原则,因为功能拆得细,所以跟自身无关的业务就不再包含到自身,比如上述的 B1 就跟 B2 没关系,那么其他模块使用 B1 的时候,也不会跟 B2 有关联,这不正是最少知识原则的体现吗!

易变性思想

把我们的代码写成容易修改的。

优先选择容易变化的数据类型。其实这就是易变性思想的体现。

我们在写代码的时候,一定要优先设计成容易变化的,优先使用接口而非对象,优先使用集合而非单一数据。

比如,现在我有个送礼接口,那么我就定义成:

public void sendGift(long uid,Gift gift) {
    
}

其实没毛病,但是,将来有一天,产品要求可以多人送礼,也就是一个礼物可以送给多个人。

所以,我们应该是将上述的uid定义成一个集合,也就是:

public void sendGift(List<Long> uids,Gift gift) {
    
}

这样一来,送一个人还是多个人,都没有问题。

所以你看,采用可变的数据类型,是不是减少了工作量。

再看个例子:

public void handleInput(String input) {
    if("Java".equals(input)) {
        doJava();
    } else if("javascript".equals(input)) {
        doJS();
    } else if("python".equals(input)) {
        doPython();
    }
}

相信大家都写过这种if-else语句,如果条件非常多的话,那简直不要太酸爽。

这肯定不行啊,不满足开放闭合原则,那就改。

我们使用map来优化下:

// 定义一个map
Map<String, Runnable> actions = new HashMap();

// 将key和value存放在map中
actions.put("Java", {doJava()})
actions.put("javascript", {doJS()})
actions.put("python", {doPython()})

public void handleInput(String input) {
    if(actions.containsKey(input)) {
        actions.get(input).run();
    }
}

我们的函数处理是不是非常简单了,而且也很容易修改,如果将来有添加或者删除,我们只需要在map中添加或者删除就行了,不需要改其他地方,这不正是开发闭合原则吗。

再比如,依赖倒置原则要求我们尽量依赖接口、依赖抽象,而不是具体的对象。

综合上面所有的例子,List、Map以及接口,它们的共同点都是可变。

所以,我们要面向可变编程,这就是易变性思想。

这跟依赖倒置原则有啥区别呢?

依赖倒置原则只描述了对象之间的关系,而我们的代码中,除了对象还有函数,还有变量,还有模块等,而这些都可以用易变性思想来描述,或者说:易变性思想是宏观层面的依赖倒置原则。

其实,易变性思想也可以叫做面向拓展编程,或者叫面向未来编程。

代码越容易改变,将来发生改变的时候就越容易修改甚至不需要修改,这就是易变性带来的好处。

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值