此法则适合所有语言,咱们以JavaScript和Java两个角度分析一下这个东东。
一、javascript
有这样的一个页面,js、css代码都写在html页面中。
例如:gnj.html
v1版本
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title>Document</title>
8 </head>
9 <body>
10 <script>
11 document.write("高内聚低耦合demo");
12 </script>
13 <style>
14 h1 {
15 background-color: blueviolet;
16 }
17 </style>
18 <h1>标题</h1>
19 </body>
20 </html>
21
这个页面承载了多个功能:定义html需要的javascript脚本,定义html需要的css样式,还有定义页面需要显示的元素。
这样的代码编写方式就像下面两个拼拼凑凑的动物:
龙:角似鹿、头似牛、眼似虾、嘴似驴、腹似蛇、鳞似鱼、足似凤、须似人、耳似象
麋鹿:
角似鹿非鹿、鼻子似牛非牛、身体似驴非驴、尾巴似马非马
问题:代码内部比较臃肿,复用度很低。js不能被多个html复用,css也不能被多个html复用。耦合性较高。
优化后的代码,如下:v2版本
gnj.js1 document.write("高内聚低耦合demo");
h1.css
1 h1 {
2 background-color: blueviolet;
3 }
4
gnj_v2.html
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title>Document</title>
8 <script src="./gnj.js"></script>
9 <link rel="stylesheet" type="text/css" href="h1.css"/>
10 </head>
11 <body>
12
13 <h1>标题</h1>
14 </body>
15 </html>
16
高内聚:模块内的事。模块内,联系越紧密,内聚性越高。
低耦合:模块间的事,相关的操作,不再直接相互依赖调用
二、java
再来看一个java的中午吃饭过程的例子:
v0版本
1 package com.gavin.controller;
2
3 import org.springframework.web.bind.annotation.GetMapping;
4 import org.springframework.web.bind.annotation.RestController;
5
6 /**
7 * Created by gavinmiao on 2019\8\30
8 */
9 @RestController
10 public class DemoController {
11 @GetMapping("/lunch")
12 public String haveLunch(){
13 StringBuilder builder = new StringBuilder();
14 builder.append("<html>");
15 //排队
16 builder.append(String.format("%s <br/>","--------------------------------------------"));
17 builder.append(String.format("%s <br/>","(^o^)开始排队(^o^)"));
18 builder.append(String.format("%s <br/>","1只羊"));
19 builder.append(String.format("%s <br/>","2只羊"));
20 builder.append(String.format("%s <br/>","3只羊"));
21 builder.append(String.format("%s <br/>","4只羊"));
22 builder.append(String.format("%s <br/>","5只羊"));
23 builder.append(String.format("%s <br/>","6只羊"));
24 builder.append(String.format("%s <br/>","7只羊"));
25 builder.append(String.format("%s <br/>","8只羊"));
26 builder.append(String.format("%s <br/>","9只羊"));
27 builder.append(String.format("%s <br/>","(-o-)结束排队(-o-)"));
28 //点菜
29 builder.append(String.format("%s <br/>","--------------------------------------------"));
30 builder.append(String.format("%s <br/>","(^o^)开始点菜(^o^)"));
31 builder.append(String.format("%s <br/>","蒸羊羔"));
32 builder.append(String.format("%s <br/>","蒸熊掌"));
33 builder.append(String.format("%s <br/>","蒸鹿尾儿"));
34 builder.append(String.format("%s <br/>","烧花鸭"));
35 builder.append(String.format("%s <br/>","烧雏鸡"));
36 builder.append(String.format("%s <br/>","烧子鹅"));
37 builder.append(String.format("%s <br/>","卤猪"));
38 builder.append(String.format("%s <br/>","卤鸭"));
39 builder.append(String.format("%s <br/>","酱鸡"));
40 builder.append(String.format("%s <br/>","腊肉"));
41 builder.append(String.format("%s <br/>","松花"));
42 builder.append(String.format("%s <br/>","小肚儿"));
43 builder.append(String.format("%s <br/>","(-o-)结束点菜(-o-)"));
44
45 //取餐
46 builder.append(String.format("%s <br/>","--------------------------------------------"));
47 builder.append(String.format("%s <br/>","(^o^)开始取餐(^o^)"));
48 builder.append(String.format("%s <br/>","一盘蒸羊羔"));
49 builder.append(String.format("%s <br/>","一盘蒸熊掌"));
50 builder.append(String.format("%s <br/>","一盘蒸鹿尾儿"));
51 builder.append(String.format("%s <br/>","一盘烧花鸭"));
52 builder.append(String.format("%s <br/>","一盘烧雏鸡"));
53 builder.append(String.format("%s <br/>","一盘烧子鹅"));
54 builder.append(String.format("%s <br/>","一盘卤猪"));
55 builder.append(String.format("%s <br/>","一盘卤鸭"));
56 builder.append(String.format("%s <br/>","一盘酱鸡"));
57 builder.append(String.format("%s <br/>","一盘腊肉"));
58 builder.append(String.format("%s <br/>","一盘松花"));
59 builder.append(String.format("%s <br/>","一盘小肚儿"));
60 builder.append(String.format("%s <br/>","(-o-)结束取餐(-o-)"));
61
62 //用餐
63 builder.append(String.format("%s <br/>","--------------------------------------------"));
64 builder.append(String.format("%s <br/>","(^o^)开始用餐(^o^)"));
65 builder.append(String.format("%s <br/>","蒸羊羔好吃"));
66 builder.append(String.format("%s <br/>","蒸熊掌好吃"));
67 builder.append(String.format("%s <br/>","蒸鹿尾儿好吃"));
68 builder.append(String.format("%s <br/>","烧花鸭好吃"));
69 builder.append(String.format("%s <br/>","烧雏鸡好吃"));
70 builder.append(String.format("%s <br/>","烧子鹅好吃"));
71 builder.append(String.format("%s <br/>","卤猪好吃"));
72 builder.append(String.format("%s <br/>","卤鸭好吃"));
73 builder.append(String.format("%s <br/>","酱鸡好吃"));
74 builder.append(String.format("%s <br/>","腊肉好吃"));
75 builder.append(String.format("%s <br/>","松花好吃"));
76 builder.append(String.format("%s <br/>","小肚儿好吃"));
77 builder.append(String.format("%s <br/>","(-o-)结束用餐(-o-)"));
78 builder.append(String.format("%s <br/>","--------------------------------------------"));
79 builder.append("</html>");
80 return builder.toString();
81 }
82
83 }
84
代码运行如下:
仔细阅读以上代码,发现有很多重复的地方,比如分割线和添加字符串操作。基于这两个重复的地方,咱们可以优化一下。单独提供两个方法,一个获取分割线,另外一个处理字符串拼接。
V1版本
1 package com.gavin.controller;
2
3 import org.springframework.web.bind.annotation.GetMapping;
4 import org.springframework.web.bind.annotation.RestController;
5
6 /**
7 * Created by gavinmiao on 2019\8\30
8 */
9 @RestController
10 public class DemoV1Controller {
11 @GetMapping("/v1/lunch")
12 public String haveLunch(){
13 StringBuilder builder = new StringBuilder();
14 builder.append("<html>");
15 //排队
16 appendStr(builder,getSeparator());
17 appendStr(builder,"(^o^)开始排队(^o^)");
18 appendStr(builder,"1只羊");
19 appendStr(builder,"2只羊");
20 appendStr(builder,"3只羊");
21 appendStr(builder,"4只羊");
22 appendStr(builder,"5只羊");
23 appendStr(builder,"6只羊");
24 appendStr(builder,"7只羊");
25 appendStr(builder,"8只羊");
26 appendStr(builder,"9只羊");
27 appendStr(builder,"(-o-)结束排队(-o-)");
28 //点菜
29 appendStr(builder,getSeparator());
30 appendStr(builder,"(^o^)开始点菜(^o^)");
31 appendStr(builder,"蒸羊羔");
32 appendStr(builder,"蒸熊掌");
33 appendStr(builder,"蒸鹿尾儿");
34 appendStr(builder,"烧花鸭");
35 appendStr(builder,"烧雏鸡");
36 appendStr(builder,"烧子鹅");
37 appendStr(builder,"卤猪");
38 appendStr(builder,"卤鸭");
39 appendStr(builder,"酱鸡");
40 appendStr(builder,"腊肉");
41 appendStr(builder,"松花");
42 appendStr(builder,"小肚儿");
43 appendStr(builder,"(-o-)结束点菜(-o-)");
44
45 //取餐
46 appendStr(builder,getSeparator());
47 appendStr(builder,"(^o^)开始取餐(^o^)");
48 appendStr(builder,"一盘蒸羊羔");
49 appendStr(builder,"一盘蒸熊掌");
50 appendStr(builder,"一盘蒸鹿尾儿");
51 appendStr(builder,"一盘烧花鸭");
52 appendStr(builder,"一盘烧雏鸡");
53 appendStr(builder,"一盘烧子鹅");
54 appendStr(builder,"一盘卤猪");
55 appendStr(builder,"一盘卤鸭");
56 appendStr(builder,"一盘酱鸡");
57 appendStr(builder,"一盘腊肉");
58 appendStr(builder,"一盘松花");
59 appendStr(builder,"一盘小肚儿");
60 appendStr(builder,"(-o-)结束取餐(-o-)");
61
62 //用餐
63 appendStr(builder,getSeparator());
64 appendStr(builder,"(^o^)开始用餐(^o^)");
65 appendStr(builder,"蒸羊羔好吃");
66 appendStr(builder,"蒸熊掌好吃");
67 appendStr(builder,"蒸鹿尾儿好吃");
68 appendStr(builder,"烧花鸭好吃");
69 appendStr(builder,"烧雏鸡好吃");
70 appendStr(builder,"烧子鹅好吃");
71 appendStr(builder,"卤猪好吃");
72 appendStr(builder,"卤鸭好吃");
73 appendStr(builder,"酱鸡好吃");
74 appendStr(builder,"腊肉好吃");
75 appendStr(builder,"松花好吃");
76 appendStr(builder,"小肚儿好吃");
77 appendStr(builder,"(-o-)结束用餐(-o-)");
78 appendStr(builder,getSeparator());
79 builder.append("</html>");
80 return builder.toString();
81 }
82
83 private String getSeparator(){
84 return "--------------------------------------------";
85 }
86
87 private void appendStr(StringBuilder builder,String 啊我额){
88 builder.append(String.format("%s <br/>",啊我额));
89 }
90 }
91
代码运行如下:
刚刚单独处理了一下分割线,那一般分割线因人而异,爱好不同,分割线样式也不同。像这种分割线有很多种样式,怎么办呢?有的同学会想到,编写接口,提供多个实现类。对,大致思路是这样,还有一个细节同学们没想到,就是最终需要做一个决策,到底使用哪种分割线样式。这个决策,我们让controller自己来确定。
V2版本
1 package com.gavin.controller;
2
3 import com.gavin.common.SeparatorContext;
4 import com.gavin.service.GenSeparator;
5 import com.gavin.service.impl.BoLangXianSeparator;
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.web.bind.annotation.GetMapping;
8 import org.springframework.web.bind.annotation.RestController;
9
10 import javax.annotation.Resource;
11
12 /**
13 * Created by gavinmiao on 2019\8\30
14 */
15 @RestController
16 public class DemoV2Controller {
17 @Autowired
18 private SeparatorContext separatorContext;
19 @Resource
20 private GenSeparator boLangXianSeparator;
21 @Resource
22 private GenSeparator greaterThanSeparator;
23 @Resource
24 private GenSeparator hengGangSeparator;
25
26 @GetMapping("/v2/lunch")
27 public String haveLunch(){
28 StringBuilder builder = new StringBuilder();
29 builder.append("<html>");
30 //排队
31 appendStr(builder,getSeparator());
32 appendStr(builder,"(^o^)开始排队(^o^)");
33 appendStr(builder,"1只羊");
34 appendStr(builder,"2只羊");
35 appendStr(builder,"3只羊");
36 appendStr(builder,"4只羊");
37 appendStr(builder,"5只羊");
38 appendStr(builder,"6只羊");
39 appendStr(builder,"7只羊");
40 appendStr(builder,"8只羊");
41 appendStr(builder,"9只羊");
42 appendStr(builder,"(-o-)结束排队(-o-)");
43 //点菜
44 appendStr(builder,getSeparator());
45 appendStr(builder,"(^o^)开始点菜(^o^)");
46 appendStr(builder,"蒸羊羔");
47 appendStr(builder,"蒸熊掌");
48 appendStr(builder,"蒸鹿尾儿");
49 appendStr(builder,"烧花鸭");
50 appendStr(builder,"烧雏鸡");
51 appendStr(builder,"烧子鹅");
52 appendStr(builder,"卤猪");
53 appendStr(builder,"卤鸭");
54 appendStr(builder,"酱鸡");
55 appendStr(builder,"腊肉");
56 appendStr(builder,"松花");
57 appendStr(builder,"小肚儿");
58 appendStr(builder,"(-o-)结束点菜(-o-)");
59
60 //取餐
61 appendStr(builder,getSeparator());
62 appendStr(builder,"(^o^)开始取餐(^o^)");
63 appendStr(builder,"一盘蒸羊羔");
64 appendStr(builder,"一盘蒸熊掌");
65 appendStr(builder,"一盘蒸鹿尾儿");
66 appendStr(builder,"一盘烧花鸭");
67 appendStr(builder,"一盘烧雏鸡");
68 appendStr(builder,"一盘烧子鹅");
69 appendStr(builder,"一盘卤猪");
70 appendStr(builder,"一盘卤鸭");
71 appendStr(builder,"一盘酱鸡");
72 appendStr(builder,"一盘腊肉");
73 appendStr(builder,"一盘松花");
74 appendStr(builder,"一盘小肚儿");
75 appendStr(builder,"(-o-)结束取餐(-o-)");
76
77 //用餐
78 appendStr(builder,getSeparator());
79 appendStr(builder,"(^o^)开始用餐(^o^)");
80 appendStr(builder,"蒸羊羔好吃");
81 appendStr(builder,"蒸熊掌好吃");
82 appendStr(builder,"蒸鹿尾儿好吃");
83 appendStr(builder,"烧花鸭好吃");
84 appendStr(builder,"烧雏鸡好吃");
85 appendStr(builder,"烧子鹅好吃");
86 appendStr(builder,"卤猪好吃");
87 appendStr(builder,"卤鸭好吃");
88 appendStr(builder,"酱鸡好吃");
89 appendStr(builder,"腊肉好吃");
90 appendStr(builder,"松花好吃");
91 appendStr(builder,"小肚儿好吃");
92 appendStr(builder,"(-o-)结束用餐(-o-)");
93 appendStr(builder,getSeparator());
94 builder.append("</html>");
95 return builder.toString();
96 }
97
98 private String getSeparator(){
99 //return separatorContext.getSeparator(boLangXianSeparator);
100 //return separatorContext.getSeparator(hengGangSeparator);
101 return separatorContext.getSeparator(greaterThanSeparator);
102 }
103
104 private void appendStr(StringBuilder builder,String 啊我额){
105 builder.append(String.format("%s <br/>",啊我额));
106 }
107 }
108
代码运行如下:
前3个版本我们只是处理了一下整个吃饭过程中的小细节。
真正的吃饭过程的代码还是很长的,得翻好多屏,并且排队、点菜、取餐、用餐,4块逻辑,顺序执行,单独某一块比较独立。另一个是,没使用上MVC分层思想,应该将业务代码放到业务层中。这样controller中的代码就很少了。业务层,我们也可以按业务功能细分一下,针对controller中出现的4块逻辑,各自创建一个Service类。这样就完美的解决了MVC问题与代码长的问题了。
最后一个问题,字符串处理属于公共逻辑,可以把它抽取到一个StringUtil的公共类中,供Controller和Service调用。
V3版本
1 package com.gavin.controller;
2
3 import com.gavin.common.SeparatorContext;
4 import com.gavin.common.StringUtil;
5 import com.gavin.service.*;
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.web.bind.annotation.GetMapping;
8 import org.springframework.web.bind.annotation.RestController;
9
10 import javax.annotation.Resource;
11
12 /**
13 * Created by gavinmiao on 2019\8\30
14 */
15 @RestController
16 public class DemoV3Controller {
17 @Autowired
18 private SeparatorContext separatorContext;
19 @Resource
20 private GenSeparator boLangXianSeparator;
21 @Resource
22 private GenSeparator greaterThanSeparator;
23 @Resource
24 private GenSeparator hengGangSeparator;
25
26 @Autowired
27 private OrderService orderService;
28 @Autowired
29 private QueueService queueService;
30 @Autowired
31 private TakeFoodService takeFoodService;
32 @Autowired
33 private HaveDinnerService haveDinnerService;
34
35
36 @GetMapping("/v3/lunch")
37 public String haveLunch(){
38 StringBuilder builder = new StringBuilder();
39 builder.append("<html>");
40 StringUtil.appendStr(builder,getSeparator());
41 StringUtil.appendStr(builder,queueService.execute());
42 StringUtil.appendStr(builder,getSeparator());
43 StringUtil.appendStr(builder,orderService.execute());
44 StringUtil.appendStr(builder,getSeparator());
45 StringUtil.appendStr(builder,takeFoodService.execute());
46 StringUtil.appendStr(builder,getSeparator());
47 StringUtil.appendStr(builder,haveDinnerService.execute());
48 StringUtil.appendStr(builder,getSeparator());
49 builder.append("</html>");
50 return builder.toString();
51 }
52
53 private String getSeparator(){
54 //return separatorContext.getSeparator(boLangXianSeparator);
55 //return separatorContext.getSeparator(hengGangSeparator);
56 return separatorContext.getSeparator(greaterThanSeparator);
57 }
58
59
60 }
61
代码运行如下:
从这4个版本中可以感受到,出现拼拼凑凑的感觉时,那么你的代码就是内聚性比较低的表现了。如果代码总要变来变去,其实是耦合高的表现。
最后,想要提高内聚性,可以通过降低耦合度来达到目的。在这儿,我个人提倡同学们编写高内聚、低耦合的代码。
【原创文章,转载请注明出处! GavinMiao】
Gavin