Lab1–Dependency Analysis and Dependency Graph


小组成员信息

  • 毛顿 20183690024

  • 欧洲 201836900207

  • 杨晗涵 201836900210

  • 刘威 201836900222

  • 来锦韬 201836900220

项目GitHub地址

EnglishPal

项目Read The Docs地址

Read The Docs

Abstract

借助相关开源软件对EnglishPal进行依赖分析。

Introduction

EnglishPal是一款致力于为用户提供方便快捷英语学习服务的网页应用。它提供了“单词本”的功能,可供用户查找添加单词,并根据用户的实际英语水平为其推荐合适难度的英语文章。

此次实验我们主要对EnglishPal的体系结构进行分析。一方面从模块构造的角度,探讨该软件文件间的依赖关系;另一方面从类/函数的角度,深入剖析文件内部类与函数的相互作用,理解软件的运行逻辑。

Materials and Methods

Materials

Methods

  • 借助开源软件,分析并绘制出模块依赖图。

  • 阅读源码,手动绘制类与函数的依赖图并简单描述。

  • 对软件进行调试运行,加深理解。

Results

模块层(module-level)

snakefood.dot:

# This file was generated by sfood-graph.

strict digraph "dependencies" {
    graph [
        rankdir = "LR",
        overlap = "scale",
        size = "8,10",
        ratio = "fill",
        fontsize = "16",
        fontname = "Helvetica",
        clusterrank = "local"
        ]

       node [
           fontsize=12
           shape=ellipse
//           style=filled
//           shape=box
       ];

//     node [
//         fontsize=7
//       style=ellipse
//     ];

"UseSqlite.py"  [style=filled];
"UseSqlite.py" -> "sqlite3";
"WordFreq.py"  [style=filled];
"WordFreq.py" -> "wordfreqCMD.py";
"WordFreq.py" -> "string.py";
"difficulty.py"  [style=filled];
"difficulty.py" -> "wordfreqCMD.py";
"difficulty.py" -> "math";
"difficulty.py" -> "pickle.py";
"main.py"  [style=filled];
"main.py" -> "UseSqlite.py";
"main.py" -> "WordFreq.py";
"main.py" -> "difficulty.py";
"main.py" -> "pickle_idea.py";
"main.py" -> "pickle_idea2.py";
"main.py" -> "wordfreqCMD.py";
"main.py" -> "datetime";
"main.py" -> "glob.py";
"main.py" -> "os.py";
"main.py" -> "random.py";
"pickle_idea.py"  [style=filled];
"pickle_idea.py" -> "pickle.py";
"pickle_idea2.py"  [style=filled];
"pickle_idea2.py" -> "datetime";
"pickle_idea2.py" -> "pickle.py";
"wordfreqCMD.py"  [style=filled];
"wordfreqCMD.py" -> "pickle_idea.py";
"wordfreqCMD.py" -> "collections.py";
"wordfreqCMD.py" -> "operator";
"wordfreqCMD.py" -> "os.py";
"wordfreqCMD.py" -> "string.py";
"wordfreqCMD.py" -> "sys";


}

依赖图(由Graphviz Online生成)

_images/module.png

类/函数层(class-function-level)

mermaid.txt:

classDiagram
    WordFreq ..> WordfreqCMD
    WordfreqCMD <.. Difficulty
    Sqlite3Template <|-- InsertQuery
    Sqlite3Template <|-- RecordQuery
    WordManipulation <.. GetTodayArticle
    GetTodayArticle ..|>Sqlite3Template
    Difficulty <|.. GetTodayArticle
    MainPage ..|> WordFreq
    WordManipulation <.. MarkWord
    PickleIdea <|.. WordManipulation
    MarkWord <-- MainPage
    UserMarkWord <-- UserPage
    UserReset <-- UserPage
    GetTodayArticle <.. UserPage
    UserPage ..|> PickleIdea2
    WordFreq <|.. UserPage
    WordManipulation <.. UserMarkWord
    Signup --|> UserManipulation
    Login  --|> UserManipulation
    Logout  --|> UserManipulation
    UserManipulation ..|>Sqlite3Template
    Sqlite3Template <|-- InsertQuery
    Sqlite3Template <|-- RecordQuery
    class PickleIdea{
       %%保存单词和词频
       +lst2dict(lst, d)
       +dict2lst(d)
       +merge_frequency(lst1, lst2)
       +load_record(pickle_fname)
       +save_frequency_to_pickle(d, pickle_fname)
    }
    class PickleIdea2{
       %%保存单词和日期
       +lst2dict(lst, d)
       +dict2lst(d)
       +merge_frequency(lst1, lst2)
       +load_record(pickle_fname)
       +save_frequency_to_pickle(d, pickle_fname)
    }

    class WordfreqCMD{
      %%处理文章字符串的工具类
      +freq(string)
      +youdao_link(string)
      +file2str(fname)
      +remove_punctuation(string)
      +sort_in_descending_order(List)
      +sort_in_ascending_order(List)
      +make_html_page (List,fname)
    }
    class WordFreq{
      %%初始化文章,获得词频
      %%依赖WordfreqCMD
      +init()
      +get_freq()
    }
    class Difficulty{
      %%计算文章、用户level,返回难度等级
      %%依赖WordfreqCMD
      +load_record(fname)
      +difficulty_level_from_frequency(word, d)
      +get_difficulty_level(d1, d2)
      +revert_dict(d)
      +user_difficulty_level(d_user, d)
      +text_difficulty_level(s, d)
    }
    class WordManipulation{
      +String path_prefix
      +load_freq_history(path)
      +get_time()
    }
    class GetTodayArticle{
      +dict dic
      +list article
      +within_range(x,y,r)
      +get_question_part(s)
      +get_answer_part(s)
    }
    class MainPage{
    %%依赖WordFreq
    %%拥有MarkWord
    +get_random_image()
    +get_random_ads()
    }
    class MarkWord{
    %%用于mainpage下标记单词
    }
    class UserPage{
    +date user_expiry_date
    +String username
    +String user_freq_record
    +appears_in_test(word,d)
    }
    class UserReset{
    %%没有匹配文章时,重定向到userpage
    }
    class UserMarkWord{
    %%用于userpage下标记单词
    }
    class UserManipulation{
    +String username
    +String password
    +bool available
    +bool verified
    +verify_user(username, password)
    }
    class Signup{
    +check_username_availability(username)
    +add_user(username, password)
    }
    class Login{
    +get_expiry_date(username)
    }
    class Logout{
    }
    class Sqlite3Template{
       %%连接数据库
       +__init__(self, db_fname)
       +connect(self, db_fname)
       +instructions(self, query_statement)
       +operate(self):
       +format_results(self)
       +do(self)
    }
    class InsertQuery{
      %%插入数据
      +instructions(self, query)
    }
    class RecordQuery{
      %%记录数据
      +instructions(self, query)
      +format_results(self)
      +get_results(self)
    }

依赖图(由Mermaid Live Editor生成)

_images/class-function-min.jpg

Discussions

依赖分析

业务逻辑

main.py中,我们将类别大致分为(1)单词操作类WordManipulation、(2)文章操作类GetTodayArticle、两个页面类(3)Mainpage和(4)Userpage以及(5)用户类UserManipulation.

在页面类中,MarkWord作为在Mainpage页面下标记单词,仅服务于Mainpage,二者是拥有者与被拥有者的关系;

同样,UserMarkWord作为在Userpage页面下标记单词和UserReset在用户没有匹配文章时重定向回Userpage,仅服务于Userpage,也是拥有者与被拥有者的关系。 Userpage中还需显示匹配的文章,要用到WordManipulation,因此Userpage依赖于WordManipulation,是使用者与被使用者的关系。

WordManipulation作为对单词的操作类,拥有load_freq_history、get_time等方法,用于加载词频、获取时间。 而MarkWord、UserMarkWord、GetTodayArticle三个类作为页面中对单词进行操作都依赖于WordManipulation,是使用者与被使用者的关系。

在用户类中,UserManipulation具有用户的基本信息:username、password等,以及验证用户名和密码是否通过,用户的操作如Signup、Login、Logout都作为子类继承UserManipulation,实现具体功能。

文章推荐与单词统计

新建一个WordfreqCMD类,做为一个工具类,提供处理字符串、列表的方法。

WordFreq类依赖于WordfreqCMD类,init初始化方法预处理文章,用WordfreqCMD的remove_punctuation方法删去字符串中的标点;get_freq依赖WordfreqCMD的sort_in_descending_order方法,返回一个降序后的词频列表。

Difficulty类用于计算一篇英文文章的难度等级,user_difficulty_level方法可以计算用户等级,其依赖于WordfreqCMD的sort_in_ascending_order方法;text_difficulty_level方法可以计算文章的难度等级,其依赖于WordfreqCMD的freq方法、sort_in_descending_order方法和remove_punctuation方法。

信息查找、存储与更新

sqlite3template 类用于连接数据库,其中有insertquery和recordquery两个子类,用于插入和记录数据。 pickle_idea和pickle_idea2两个类之间没有依赖关系,分别用于单词和词频的记录与单词和日期的记录。load_record方法加载pickle文件,save_frequency_to_pickle方法将数据保存到pickle文件中,merge_frequency方法用于合并两个list。

整体依赖关系

main.py中的单词操作类WordManipulation以PickleIdea作为接口,使用了其中的load_record函数获取单词记录,WordManipulation中的markword方法使用merge_frequency添加单词记录

MainPage类实现了WordFreq类,即WordFreq类是MainPage的接口,MainPage类中创建了一个WordFreq对象,使用该对象的get_freq函数获取单词记录频率。

Sqlite3Template类分别是UserManipulation类和GetTodayArticle类的接口: UserManipulation类使用InsertQuery方法注册用户,使用RecordQuery方法验证用户名密码服务于用户登录SignUp,同时也是用这个方法检验用户注册是用户名是否重复。 GetTodayArticle类使用RecordQuery方法从数据库获取文章。

Difficulty类是GetTodayArticle类的接口,GetTodayArticle类使用get_difficulty_level方法获取当前单词频率情况的等级,使用text_difficulty_level方法获取文章等级,使用user_difficulty_level方法计算用户等级。

PickleIdea2类是UserPage类的的接口,UserPage类使用dict2lst获取历史单词频率,使用merge_frequency新增不认识的词汇,使用save_frequency_to_pickle将新增单词保存到pickle。

利弊分析

界面操作

  1. 一些文字提示与其所对应的操作之间指示性不强,如主页中“试试”是注册用户操作,这既与上面的“成为会员”操作重复,且含义不明显,用户可能误解为登录操作等。

  2. 注册账户与登录操作中密码无法可视化,用户难以确认是否有打字错误。

  3. 登录有误(login中verified为false)时,页面只显示“无法通过验证”,无进一步登录提示,用户只能通过网页上的返回上一页面操作来重新登录。

  4. 用户注册成功后,跳转的页面所含的功能有些重复且指向性不明确。如”你的用户名是XXX“,用户点击该用户名可跳转至用户页面,这与后面的”开始使用“功能一致,这里单纯显示用户名较好。且”开始使用“相对于”返回主页“也有些意味不明,改为”用户中心“等较好。

代码逻辑

  1. mainpage中”粘贴一篇文章“中输入文章并点击”get文章中的词频“后,后台已对这篇文章中的词汇进行了统计与合并(即对frequency.p进行了修改);但当用户在跳转的页面中对词汇进行勾选(即“mark”该单词)并点击“确认并返回”时,后台又对这些“marked”的词汇进行了统计,重复统计,意味不明。

体系结构

  1. EnglishPal的模块化虽然纵向维度不深,但在横向维度上较好的将各个功能从主要的业务逻辑中抽离出来,在main.py中有条不紊的调用各分模块的功能,使得整体结构较为清晰。

  2. 整体采用了经典的MVC网页应用体系结构,使得显示层、业务层与数据层彼此间相互较为独立,利于进一步的功能开发。

  3. 在业务逻辑代码中嵌入了大量的网页显示代码,不仅使得分析代码与维护软件困难,更提高了更新应用的难度。

References

暂时没有引用。