本帖最后由 miaoxiong 于 2019-9-2 15:21 编辑
此法则适合所有语言,咱们以JavaScript和Java两个角度分析一下这个东东。
一、javascript
有这样的一个页面,js、css代码都写在html页面中。
例如:gnj.html
v1版本
[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>
</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
[JavaScript] 纯文本查看 复制代码 document.write("高内聚低耦合demo");
h1.css
[JavaScript] 纯文本查看 复制代码 h1 {
background-color: blueviolet;
}
gnj_v2.html
[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版本
[Java] 纯文本查看 复制代码 package com.gavin.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by 传智播客.黑马程序员.太原校区.苗雄 on 2019\8\30
*/
@RestController
public 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版本
[Java] 纯文本查看 复制代码 package com.gavin.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by 传智播客.黑马程序员.太原校区.苗雄 on 2019\8\30
*/
@RestController
public 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版本
[Java] 纯文本查看 复制代码 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
*/
@RestController
public class DemoV2Controller {
@Autowired
private SeparatorContext separatorContext;
@Resource
private GenSeparator boLangXianSeparator;
@Resource
private GenSeparator greaterThanSeparator;
@Resource
private 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版本
[Java] 纯文本查看 复制代码 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
*/
@RestController
public class DemoV3Controller {
@Autowired
private SeparatorContext separatorContext;
@Resource
private GenSeparator boLangXianSeparator;
@Resource
private GenSeparator greaterThanSeparator;
@Resource
private GenSeparator hengGangSeparator;
@Autowired
private OrderService orderService;
@Autowired
private QueueService queueService;
@Autowired
private TakeFoodService takeFoodService;
@Autowired
private 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个版本中可以感受到,出现拼拼凑凑的感觉时,那么你的代码就是内聚性比较低的表现了。如果代码总要变来变去,其实是耦合高的表现。
最后,想要提高内聚性,可以通过降低耦合度来达到目的。在这儿,我个人提倡同学们编写高内聚、低耦合的代码。 |
|