暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

编码至高法则- 高内聚 低耦合

v587博客 2021-08-24
667


                                                                        喜欢就关注我们吧!


编码至高法则

此法则适合所有语言,咱们以 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 海文
            */
            @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 版本

              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 版本

                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 版本

                  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 个版本中可以感受到,出现拼拼凑凑的感觉时,那么你的代码就是内聚性比较低的表现了。如果代码总要变来变去,其实是耦合高的表现。

                      最后,想要提高内聚性,可以通过降低耦合度来达到目的。在这儿,我个人提倡同学们编写高内聚、低耦合的代码。




                  ☆ END ☆



                  壹伴精选



                  热文

                  GCRoot对象

                  更多

                  围观

                  第一篇日记

                  更多





                  扫描二维码

                  获取更多精彩

                  苗雄博客







                  文章转载自v587博客,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论