Page 225 - 《软件学报》2021年第8期
P. 225

黄子杰  等:检测 JavaScript 类的内聚耦合 Code Smell                                          2507


                                                              [7]
                 实现参考了 Fowler 的影音店案例       [9,10] 和 JS 类检测的文献 .

                       1.   public class Rental {
                       2.      private Movie movie;            1.   function Rental(data){
                       3.      private int daysRented;         2.      this._data=data;
                       4.      public Rental(Movie movie,int daysRented){   3.   }
                       5.        this.movie=movie;             4.   Rental.prototype.getMovie=function(⋅){
                       6.        this.daysRented=daysRented;      5.      return this._data_.movie;
                       7.      }                               6.   }
                       8.      public int getDaysRented(⋅){return daysRented;}     7.   Rental.prototype.getDaysRented=function(⋅){
                       9.      public Movie getMovie(⋅){return movie;}   8.      return this._data_.daysRented;
                       10.   }                                 9.   }

                                     (a)  一个 Java 类              (b) ES2015 标准发布前的一种 JS 类设计模式

                                             1.   class Rental{

                                             2.      constructor(data){this._data=data;}
                                             3.      get daysRented(⋅) {

                                             4.        return this._data_daysRented;
                                             5.      }
                                             6.      get movie(⋅){
                                             7.        return this._data.movie;
                                             8.      }
                                             9.   }

                                                 (c)  一个利用糖衣语法实现的 JS 类
                                 Fig.1    Demonstration code showing Java and JS class implementations
                                              图 1   Java 和 JS 类的实现代码示例

                    JS 类是基于 JS 原型继承(prototype-based inheritance)构建的一种经典模型      [11] ,它的实现可分为两类    [12] .
                    (1)  在早于 ES2015 的语言标准中,类是基于函数的设计模式,实现方式不唯一,图 1(b)是一种常见的实现.
                    (2)  如图 1(c),ES2015 及更新的标准用糖衣语法(syntactic sugar)简化了类的实现方式.
                    检测 JS 类首先需要定位构造器(constructor,例如图 1 中的灰色高亮部分)和成员属性(例如_data),再遍历函
                 数的原型链(例如图 1(b)的 Rental.prototype)搜索其成员函数和子类.构造器是类的显著特征,可用于创建对象.
                 定位构造器,可通过查找类的实现函数,或查找构造器的调用语句(例如 new 语句).目前,已有效果较好的、基于
                                                                        [7]
                 抽象语法树遍历的开源 JS 类检测工具 JSDeodorant         [13] 、JSClassFinder .
                    本文需要检测的 JS 类信息为类的成员(即函数和属性)、类的函数及其签名、类引用的外部数据提供者
                 (foreign data provider,简称 FDP)和类引用的内部成员.某类的“外部数据”指的是软件系统中非本类的类成员.分
                 析引用关系,可通过遍历代码中的访问操作(access)实现.访问操作是指对对象的引用、对其成员属性的引用
                 (reference)或对其成员函数的调用(call).
                    检测 JS 类及相关信息的挑战包括:
                    (1)  检测难以适用于所有的类实现方式.实现类的设计模式,有至少 5 种较为常用的、适用于不同版本语
                        言标准的方式      [7,13] ,给软件组件的判定和标准化带来困难.
                    (2)  检测类与类间的关系困难.受 JS 的语言特性限制,类成员属性的类型在静态分析过程中是未知的.类
                        型信息的缺失,导致分析引用关系的困难.这种缺失无法使用动态分析完全弥补,因其依赖爬虫,不适
                        用于运行在服务端的 JS 程序.
                 1.2   结构分析
                    结构分析是最常见的 Code Smell 检测方法,通常包括 2 个阶段,即信息收集和检测判定:在信息收集阶段,
                 分析工具通过遍历抽象语法树获取检测对象的结构信息;在检测判定阶段,需要根据度量的定义将收集得到的
                 信息计算为度量值,再依据规则判定是否存在 Code Smell.规则根据 Code Smell 的定义设计,它通常由一系列度
   220   221   222   223   224   225   226   227   228   229   230