喜欢就关注我们吧!
此法则适合所有语言,咱们以 JavaScript 和 Java 两个角度分析一下这个东东。
一、javascript
有这样的一个页面,js、css 代码都写在 html 页面中。
例如:gnj.html
v1 版本
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><script>document.write("高内聚低耦合 demo");</script><style>h1 {background-color: blueviolet;}</style><h1>标题</h1></body></html>
这个页面承载了多个功能:定义 html 需要的 javascript 脚本,定义 html 需要的 css 样式,还有定义页面需要显示的元素。
这样的代码编写方式,好比在一个大开间里做饭、吃饭、睡觉、洗漱等等。
问题:代码内部比较臃肿,复用度很低。js 不能被多个 html 复用,css 也不能被多个 html
复用。耦合性较高。
优化后的代码,如下:
v2 版本
gnj.js
document.write("高内聚低耦合 demo");
h1.css
h1 {background-color: blueviolet;}
gnj_v2.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script src="./gnj.js"></script><link rel="stylesheet" type="text/css" href="h1.css"/></head><body><h1>标题</h1></body></html>
高内聚:模块内的事。模块内,联系越紧密,内聚性越高。
低耦合:模块间的事,相关的操作,不再直接相互依赖调用
二、java
再来看一个 java 的中午吃饭过程的例子:
v0 版本
package com.gavin.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;/*** Created by 海文*/@RestControllerpublic class DemoController {@GetMapping("/lunch")public String haveLunch(){StringBuilder builder = new StringBuilder();builder.append("<html>");//排队builder.append(String. format ("%s<br/>","--------------------------------------------"));builder.append(String. format ("%s <br/>","(^o^)开始排队(^o^)"));builder.append(String. format ("%s <br/>","1 只羊"));builder.append(String. format ("%s <br/>","2 只羊"));builder.append(String. format ("%s <br/>","3 只羊"));builder.append(String. format ("%s <br/>","4 只羊"));builder.append(String. format ("%s <br/>","5 只羊"));builder.append(String. format ("%s <br/>","6 只羊"));builder.append(String. format ("%s <br/>","7 只羊"));builder.append(String. format ("%s <br/>","8 只羊"));builder.append(String. format ("%s <br/>","9 只羊"));builder.append(String. format ("%s <br/>","(-o-)结束排队(-o-)"));//点菜builder.append(String. format ("%s<br/>","--------------------------------------------"));builder.append(String. format ("%s <br/>","(^o^)开始点菜(^o^)"));builder.append(String. format ("%s <br/>","蒸羊羔"));builder.append(String. format ("%s <br/>","蒸熊掌"));builder.append(String. format ("%s <br/>","蒸鹿尾儿"));builder.append(String. format ("%s <br/>","烧花鸭"));builder.append(String. format ("%s <br/>","烧雏鸡"));builder.append(String. format ("%s <br/>","烧子鹅"));builder.append(String. format ("%s <br/>","卤猪"));builder.append(String. format ("%s <br/>","卤鸭"));builder.append(String. format ("%s <br/>","酱鸡"));builder.append(String. format ("%s <br/>","腊肉"));builder.append(String. format ("%s <br/>","松花"));builder.append(String. format ("%s <br/>","小肚儿"));builder.append(String. format ("%s <br/>","(-o-)结束点菜(-o-)"));//取餐builder.append(String. format ("%s<br/>","--------------------------------------------"));builder.append(String. format ("%s <br/>","(^o^)开始取餐(^o^)"));builder.append(String. format ("%s <br/>","一盘蒸羊羔"));builder.append(String. format ("%s <br/>","一盘蒸熊掌"));builder.append(String. format ("%s <br/>","一盘蒸鹿尾儿"));builder.append(String. format ("%s <br/>","一盘烧花鸭"));builder.append(String. format ("%s <br/>","一盘烧雏鸡"));builder.append(String. format ("%s <br/>","一盘烧子鹅"));builder.append(String. format ("%s <br/>","一盘卤猪"));builder.append(String. format ("%s <br/>","一盘卤鸭"));builder.append(String. format ("%s <br/>","一盘酱鸡"));builder.append(String. format ("%s <br/>","一盘腊肉"));builder.append(String. format ("%s <br/>","一盘松花"));builder.append(String. format ("%s <br/>","一盘小肚儿"));builder.append(String. format ("%s <br/>","(-o-)结束取餐(-o-)"));//用餐builder.append(String. format ("%s<br/>","--------------------------------------------"));builder.append(String. format ("%s <br/>","(^o^)开始用餐(^o^)"));builder.append(String. format ("%s <br/>","蒸羊羔好吃"));builder.append(String. format ("%s <br/>","蒸熊掌好吃"));builder.append(String. format ("%s <br/>","蒸鹿尾儿好吃"));builder.append(String. format ("%s <br/>","烧花鸭好吃"));builder.append(String. format ("%s <br/>","烧雏鸡好吃"));builder.append(String. format ("%s <br/>","烧子鹅好吃"));builder.append(String. format ("%s <br/>","卤猪好吃"));builder.append(String. format ("%s <br/>","卤鸭好吃"));builder.append(String. format ("%s <br/>","酱鸡好吃"));builder.append(String. format ("%s <br/>","腊肉好吃"));builder.append(String. format ("%s <br/>","松花好吃"));builder.append(String. format ("%s <br/>","小肚儿好吃"));builder.append(String. format ("%s <br/>","(-o-)结束用餐(-o-)"));builder.append(String. format ("%s<br/>","--------------------------------------------"));builder.append("</html>");return builder.toString();}}
代码运行如下:

仔细阅读以上代码,发现有很多重复的地方,比如分割线和添加字符串操作。基于这两个重复的地方,咱们可以优化一下。单独提供两个方法,一个获取分割线,另外一个处理字符串
拼接。
v1 版本
package com.gavin.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;/*** Created by 海文 on 2019\8\30*/@RestControllerpublic class DemoV1Controller {@GetMapping("/v1/lunch")public String haveLunch(){StringBuilder builder = new StringBuilder();builder.append("<html>");//排队appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始排队(^o^)");appendStr(builder,"1 只羊");appendStr(builder,"2 只羊");appendStr(builder,"3 只羊");appendStr(builder,"4 只羊");appendStr(builder,"5 只羊");appendStr(builder,"6 只羊");appendStr(builder,"7 只羊");appendStr(builder,"8 只羊");appendStr(builder,"9 只羊");appendStr(builder,"(-o-)结束排队(-o-)");//点菜appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始点菜(^o^)");appendStr(builder,"蒸羊羔");appendStr(builder,"蒸熊掌");appendStr(builder,"蒸鹿尾儿");appendStr(builder,"烧花鸭");appendStr(builder,"烧雏鸡");appendStr(builder,"烧子鹅");appendStr(builder,"卤猪");appendStr(builder,"卤鸭");appendStr(builder,"酱鸡");appendStr(builder,"腊肉");appendStr(builder,"松花");appendStr(builder,"小肚儿");appendStr(builder,"(-o-)结束点菜(-o-)");//取餐appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始取餐(^o^)");appendStr(builder,"一盘蒸羊羔");appendStr(builder,"一盘蒸熊掌");appendStr(builder,"一盘蒸鹿尾儿");appendStr(builder,"一盘烧花鸭");appendStr(builder,"一盘烧雏鸡");appendStr(builder,"一盘烧子鹅");appendStr(builder,"一盘卤猪");appendStr(builder,"一盘卤鸭");appendStr(builder,"一盘酱鸡");appendStr(builder,"一盘腊肉");appendStr(builder,"一盘松花");appendStr(builder,"一盘小肚儿");appendStr(builder,"(-o-)结束取餐(-o-)");//用餐appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始用餐(^o^)");appendStr(builder,"蒸羊羔好吃");appendStr(builder,"蒸熊掌好吃");appendStr(builder,"蒸鹿尾儿好吃");appendStr(builder,"烧花鸭好吃");appendStr(builder,"烧雏鸡好吃");appendStr(builder,"烧子鹅好吃");appendStr(builder,"卤猪好吃");appendStr(builder,"卤鸭好吃");appendStr(builder,"酱鸡好吃");appendStr(builder,"腊肉好吃");appendStr(builder,"松花好吃");appendStr(builder,"小肚儿好吃");appendStr(builder,"(-o-)结束用餐(-o-)");appendStr(builder,getSeparator());builder.append("</html>");return builder.toString();}private String getSeparator(){return "--------------------------------------------";}private void appendStr(StringBuilder builder,String 啊我额){builder.append(String. format ("%s <br/>",啊我额));}}
代码运行如下:

刚刚单独处理了一下分割线,那一般分割线因人而异,爱好不同,分割线样式也不同。像这种分割线有很多种样式,怎么办呢?有的同学会想到,编写接口,提供多个实现类。对,大致思路是这样,还有一个细节同学们没想到,就是最终需要做一个决策,到底使用哪种分割线样式。这个决策,我们让 controller 自己来确定。
v2 版本
package com.gavin.controller;import com.gavin.common.SeparatorContext;import com.gavin.service.GenSeparator;import com.gavin.service.impl.BoLangXianSeparator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Created by 海文 on 2019\8\30*/@RestControllerpublic class DemoV2Controller {@Autowiredprivate SeparatorContext separatorContext;@Resourceprivate GenSeparator boLangXianSeparator;@Resourceprivate GenSeparator greaterThanSeparator;@Resourceprivate GenSeparator hengGangSeparator;@GetMapping("/v2/lunch")public String haveLunch(){StringBuilder builder = new StringBuilder();builder.append("<html>");//排队appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始排队(^o^)");appendStr(builder,"1 只羊");appendStr(builder,"2 只羊");appendStr(builder,"3 只羊");appendStr(builder,"4 只羊");appendStr(builder,"5 只羊");appendStr(builder,"6 只羊");appendStr(builder,"7 只羊");appendStr(builder,"8 只羊");appendStr(builder,"9 只羊");appendStr(builder,"(-o-)结束排队(-o-)");//点菜appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始点菜(^o^)");appendStr(builder,"蒸羊羔");appendStr(builder,"蒸熊掌");appendStr(builder,"蒸鹿尾儿");appendStr(builder,"烧花鸭");appendStr(builder,"烧雏鸡");appendStr(builder,"烧子鹅");appendStr(builder,"卤猪");appendStr(builder,"卤鸭");appendStr(builder,"酱鸡");appendStr(builder,"腊肉");appendStr(builder,"松花");appendStr(builder,"小肚儿");appendStr(builder,"(-o-)结束点菜(-o-)");//取餐appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始取餐(^o^)");appendStr(builder,"一盘蒸羊羔");appendStr(builder,"一盘蒸熊掌");appendStr(builder,"一盘蒸鹿尾儿");appendStr(builder,"一盘烧花鸭");appendStr(builder,"一盘烧雏鸡");appendStr(builder,"一盘烧子鹅");appendStr(builder,"一盘卤猪");appendStr(builder,"一盘卤鸭");appendStr(builder,"一盘酱鸡");appendStr(builder,"一盘腊肉");appendStr(builder,"一盘松花");appendStr(builder,"一盘小肚儿");appendStr(builder,"(-o-)结束取餐(-o-)");//用餐appendStr(builder,getSeparator());appendStr(builder,"(^o^)开始用餐(^o^)");appendStr(builder,"蒸羊羔好吃");appendStr(builder,"蒸熊掌好吃");appendStr(builder,"蒸鹿尾儿好吃");appendStr(builder,"烧花鸭好吃");appendStr(builder,"烧雏鸡好吃");appendStr(builder,"烧子鹅好吃");appendStr(builder,"卤猪好吃");appendStr(builder,"卤鸭好吃");appendStr(builder,"酱鸡好吃");appendStr(builder,"腊肉好吃");appendStr(builder,"松花好吃");appendStr(builder,"小肚儿好吃");appendStr(builder,"(-o-)结束用餐(-o-)");appendStr(builder,getSeparator());builder.append("</html>");return builder.toString();}private String getSeparator(){//return separatorContext.getSeparator(boLangXianSeparator);//return separatorContext.getSeparator(hengGangSeparator);return separatorContext.getSeparator(greaterThanSeparator);}private void appendStr(StringBuilder builder,String 啊我额){builder.append(String. format ("%s <br/>",啊我额));}}
代码运行如下:

前 3 个版本我们只是处理了一下整个吃饭过程中的小细节。
真正的吃饭过程的代码还是很长的,得翻好多屏,并且排队、点菜、取餐、用餐,4 块逻辑,顺序执行,单独某一块比较独立。另一个是,没使用上 MVC 分层思想,应该将业务代码放到业务层中。这样 controller 中的代码就很少了。业务层,我们也可以按业务功能细分一下,针对 controller 中出现的 4 块逻辑,各自创建一个 Service 类。这样就完美的解决了MVC 问题与代码长的问题了。最后一个问题,字符串处理属于公共逻辑,可以把它抽取到一个 StringUtil 的公共类中,供 Controller 和 Service 调用。
v3 版本
package com.gavin.controller;import com.gavin.common.SeparatorContext;import com.gavin.common.StringUtil;import com.gavin.service.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** Created by 海文 on 2019\8\30*/@RestControllerpublic class DemoV3Controller {@Autowiredprivate SeparatorContext separatorContext;@Resourceprivate GenSeparator boLangXianSeparator;@Resourceprivate GenSeparator greaterThanSeparator;@Resourceprivate GenSeparator hengGangSeparator;@Autowiredprivate OrderService orderService;@Autowiredprivate QueueService queueService;@Autowiredprivate TakeFoodService takeFoodService;@Autowiredprivate HaveDinnerService haveDinnerService;@GetMapping("/v3/lunch")public String haveLunch(){StringBuilder builder = new StringBuilder();builder.append("<html>");StringUtil. appendStr (builder,getSeparator());StringUtil. appendStr (builder,queueService.execute());StringUtil. appendStr (builder,getSeparator());StringUtil. appendStr (builder,orderService.execute());StringUtil. appendStr (builder,getSeparator());StringUtil. appendStr (builder,takeFoodService.execute());StringUtil. appendStr (builder,getSeparator());StringUtil. appendStr (builder,haveDinnerService.execute());StringUtil. appendStr (builder,getSeparator());builder.append("</html>");return builder.toString();}private String getSeparator(){//return separatorContext.getSeparator(boLangXianSeparator);//return separatorContext.getSeparator(hengGangSeparator);return separatorContext.getSeparator(greaterThanSeparator);}}
代码运行如下:

从这 4 个版本中可以感受到,出现拼拼凑凑的感觉时,那么你的代码就是内聚性比较低的表现了。如果代码总要变来变去,其实是耦合高的表现。
最后,想要提高内聚性,可以通过降低耦合度来达到目的。在这儿,我个人提倡同学们编写高内聚、低耦合的代码。
壹伴精选
热文
丨更多
围观
丨更多
扫描二维码
获取更多精彩
苗雄博客






