设计坏味道

2019-06-25  本文已影响0人  程序猿春天

设计坏味道

1.  设计坏味道概述

为何要关注坏味道,因为坏味道影响了软件的质量;给项目的开发、维护、扩展等带来了影响;

A.    软件质量

              i.        可理解性

1.    代码理解的难易程度;

            ii.        可以修改性

1.    修改代码时,不会导致连锁反应;

            iii.        可扩展性

1.    增加新功能,不会导致连锁反应;

            iv.        可重用性

1.    代码可以在相同问题域中,直接复用;

            v.        可测试性

1.    支持单元测试

            vi.        可靠性

1.    正确实现功能情况下,支持容错性;                                   

2.  坏味道分类

3.  抽象性

A.    缺失抽象

Example:

_ 问题点在JDK1.0中方法printStackTrace()以字符串的方式将栈跟踪打印到标准错误流:

      public classThrowabe{  public voidprintStackTrace();

      }

      在需要以编程方式访问栈跟踪元素的客户程序中,必须要编程代码来获取数据,如行号等,由于客户程度依赖这种字符串格式,JDK设计人员只能在后续版本中兼容这种格式了。

_ 解决方法

      public classThrowabe{  public voidprintStackTrace(); public                                    StackTraceElement[]getStackTrace();

      }

从Jdk1.4起对JAVA的API进行了改进,StackTraceElement类就是原来设计中缺失的对象,定义如下:

      public finalclassStackTraceElement{

          public final classStackTraceElement{

            public StringgetFilename();

          publicintgetLineNumber();

          public StringgetClassname();

            ......

            }

Example:

_ 问题点

  细粒度的异常处理问题,以前见过系统的异常类只是继承Exception,只保存了message信息;因此如果需要细分并根据不同类型,不同的级别急性控制时,就比较麻烦;

_ 解决方法

_ 细分异常类型

_ 增加errorcode

异常类包含了一个接口,具体异常类中,枚举类型实现该接口;

说明:使用function编程解耦业务异常类的处理,使业务类只关注业务;

B.    命令式抽像

Example:

_    问题点

    说明:每个类只包含一个方法,分别是:create、display、copy;因此存在命令式抽象,会增加类的数量、开发、维护的复杂度;而且把本该内聚的方法,分散到多个类上;没有做到内聚,而增加了耦合;

_    解决方案

根据高内聚原则,归并到一个类中;

C.    不完整抽象

                抽象未支持互补方法,导致不完成抽象,比如一个抽象接口,只有startUp,而没有stopUp方法;

常见互补方法:

D.    多方面抽象

                对象被赋予不止一种职责,违背单一职责原则;

Example:

_  问题点

java.util.Calendar

类承担了多项职责,不仅提供了日期相关的功能,还提供了与时间有关的功能,存大多方面抽象。由于同时支持日期和时间的方法,Calendar类接口很大且难为理解,在JDK7中,java.util.Calendar类包括了2825行代码,有67个方法和71个字段。

_  解决方案对于Calendar类,一种可能的重构是,将Calendar类与时间相关的功能提取到新类Time中,并将相关方法和字段移到新提取的类中,在Java8中引入了一些支持日期和时间的新类,这些类位于java.time中。

E.    不必要抽象

Example:

_  问题点

publicinterfaceWindowConstants{

public static finalintDO_NOTHING_ON_CLOSE=0;

public static finalintHIDE_ON_CLOSE=1;

}

  注:这个接口是典型的常量接口javax.swing.WindowConstants,为啥用接口来存储常量,因为首先枚举是jdk1.5才引入的,其次通过接口中定义常量,可方便类通过继承而不是委托来使用它们,因为通过实现接口,类可方便的访问接口中的常量,为什么不使用类来存储常量呢,因为接口支持多继承。

那么接口这样定义常量有哪些问题呢?

A

、派生类被无关的常量影响。

B

、这些常量属于实现细节,通过接口暴露它们违反封装原则。

C

、接口中存储常量,修改它们会影响其他使用者。

_  解决方案将WindowsConstants定义为枚举,直接使用。

F.    重复抽象

根据DRY原则规定:对于每个技术点,系统中都只能有一个明确的表示。导致重复抽象的原因有:

A

、复制-粘贴编程手法

B

、即兴维护

C

、交流不畅

Example1:

_  问题点

java.util.Date

和其派生类java.sql.Date同名,这两个类位于不同的包中,编译器不会因为它们同名而报错,但这让使用者一头雾水,这样将导致二义性。

_  解决方案将Date名称前面加上用途限定语,比如java.sql.SQLDate更合适。

Example2:

      问题点

      相同的类,包名称不一样,不同的包面向不同的使用者;

task-clent中包含com.xx.xx.task.domain.task.Task和task-server 中的com.xx.xx.task.domain.task.Task,一个抽象放到不同的模块,项目中最好也要避免这种情况;

              i.        DRY原则

用Lambda和Function解决方式:

说明:在该execute基础上实现单、宽index的cud操作;

G.    书籍推荐:

<<软件设计重构>>

4.  总结本次只分享抽象性的坏味道,欢迎感兴趣的同事,结合自己的理解和实践,继续分享其他模块;

上一篇下一篇

猜你喜欢

热点阅读