OO第一次单元总结

第一次总结性博客

16071070 陈泽寅 2019.3.23

一、第一单元所学总结

  • 首先先来总结一下第一单元我所学到的知识以及所感所悟。第一个单元,是我第一次接触JAVA语言,并且再使用了几次之后,就被这门语言的独有的魅力以及简便的用法所深深吸引。下面我从三个方面来简单阐述一下我对于JAVA相比较于c语言的优势。
    • (1)从架构上来说,java的设计思路是不同于c的,它是一门面向对象的语言,我们的思维从熟悉的过程式编程语言转移到了对象思维上来。这样的思维的好处是,我们可以将一个大的问题分成很多个小的类去进行处理。如果说过程式编程是一个庞大的整体,而函数是其一个个功能的分布,那么在java里类就是实现各个子模块的实现者。在java的面相编程思维中,类的设计秉持高内聚,低耦合的思想。即在每个类的内部只关心自己类的操作,而不去关心其他类的事情。这样的好处是,我们把整个过程细化成很多个类去实现,每个类只需实现自己的功能,而不需关心其他类的功能。这样方便程序员在写每个模块的时候不需考虑当前类对其他类的影响,并且方便进行单元测试以及问题的发现。同时当某个需求发生改变时,只需更改相应的类,而不需去修改其他相关的类。因为类与类之间的关系是低耦合的。这样方便日后的维护与调试。
    • (2)从设计安全性的角度来说,java在大型项目开发的时候更加安全。因为每个类的属性都是private的,其他类不能直接访问当前类的私有属性,因此无法直接对属性的值进行修改。这是安全的,因为其他类可能并不知道这个类的设计原则,若直接修改类的属性可能导致bug的产生。java针对这种情况提供了public方法,其他类可以调用public方法去实现类属性的修改,而一些修改的限制都写在方法中,因此其他类无需知道这些细节,并且这些public方法也保证了类属性数据的安全性。同时java还提供了接口的思想,即很多不同的类为了实现某个接口,就能实现类与类之间的联系。这样就大大增加了程序的可扩展性和可移植性。
    • (3)java还有一个很大的优势就是其写法相当简便,相比于c它提供了大量的内置函数包以供调用,比如其String类,ArrayList类,HashMap类等等。还有一些sort,find函数,这些函数都是经过优化的方法,省去了程序员一些复杂地基本操作,使程序可读性增强。

二、多项式求导程序设计说明

  • (1)第一次作业
    第一次作业的设计,现在看来真是杂乱不堪,完全是面相过程式编程,只不过是套了一个JAVA的壳子而已,存在很多的问题。
    第一次作业的题目是求一个简单多项式的导数。所有的表达式都是形如\(f(x) = \Sigma a*x^c\)这种形式。这个多项式是一个字符串类型,当然我们首先应该判断其是否合法。我的思路是首先通过正则匹配检查是否存在不合法的空格。

    String pattern1 = ".*[+-]\\s*[+-]\\s+\\d.*";
    String pattern2= ".*[\\^]\\s*[+-]\\s+\\d.*"; 
    String pattern3 = ".*\\d+\\s+\\d+.*";

    上述的三个表达式分别判断了是否存在表达式加法时的带符号整数之间的非法空格,数字与数字之间的非法空格,以及指数后的带符号整数的非法空格。在判断不存在这样的非法空格之后,为了方便后续的项的分离,我们将字符串中的所有空格和\t制表符全都删去。
    然后我们对整个字符串进行合法性的检验,检查其每一项是否都属于\(f(x) = a*x^c\)这种形式。这里的正则表达式如下。

    pattern1 = "([+-]{0,2}(((\\d+[*])?[x]([\\^]([+-])?\\d+)?)|(\\d+)))";

    我们这里需要注意,因为其是贪婪性匹配,每次都去匹配满足上述模式的最大字符串,因此当字符串巨大时可能会存在爆栈的情况,因此我们调用Pattern Matcher里的方法将其分成一个个小的GROUP,并得到每个表达式的系数以及指数,并将其存在多项式类中

下面是第一次作业的类图
1613369-20190325161923955-139320611.jpg
1613369-20190325164823066-1101495325.jpg

第一次作业的失败主要是把所有的函数方法都写在了一个类里面,无论是数据的读取,还是表达式的构造,以及最后的输出,都是在一个Main类里,导致这个类的长度达到了几百行。这是明显违反了OO的设计准则的。正确的设计方法应该是首先构造一个读取类,这个类专门用来读取数据,并且存储我们的字符串。然后再写一个判断类,来判断我们的字符串是否合法。再通过一个分离类,将我们的字符串进行分离,将分离出来的数据存储在我们的多项式类中,最后再通过输出类来进行数据的输出。这样每个模块功能明确,并且当日后增加需求的时候,大的模块不需要变动,只需在各个类中添加或者修改方法即可。
  • (2)第二次作业:第二次作业在第一次作业的基础上增加了\(sin(x)以及cos(x)这两种幂函数\),并且在每个表达式中允许存在两个因式相乘的形式。由于第一次作业的偷懒设计,导致第二次作业的架构需要重新设计,但是在做完第三次作业之后发现第二次作业的设计依旧是有很大的弊端。

    第二次作业的多项式的形式是 \(f(x)=\Sigma a*sin(x)^b*cos(x)^c*x^d\)。项里可以存在乘法,这就需要更改之前的表达式的分离的方法。首先我先更改了多项式的存储结构,运来的多项式里只包含x的指数和系数。现在加入了\(sin(x)和cos(x)\)的指数。最后得到一个项的list,并且根据相应的公式进行求导。求导的公式是
    \(a*l*x ^ (a - 1)*cos(x)^c*sin(x)^b + b*l*x^a*cos(x)*cos(x)^c* sin(x)^(b - 1) - c*l*x^a*cos(x)^(c - 1)*sin(x)*sin(x)^b\)

    这次作业在上一次的基础上更新正则表达式的匹配样例

            String patternHead = "[+-]{0,2}(([+-]?\\d+)|(" +"[x]([\\^][+-]?\\d+)?)" +"|([s][i][n][\\(][x][\\)]([\\^][+-]?\\d+)?)" +"|([c][o][s][\\(]" +"[x][\\)]([\\^][+-]?\\d+)?))([*](([+-]?\\d+)|([x]([\\^][+-]" +"?\\d+)?)|" +"([s][i][n][\\(][x][\\)]([\\^][+-]?\\d+)?)|([c][o][s][\\(]" +"[x][\\)]([\\^][+-]?\\d+)?)))*";

    通过这个表达式去得到一个个项,然后通过split
    函数将\(*\)号分开得到一个个因式,再通过因式的匹配样例

    Pattern pattern = Pattern.compile("[+-]{0,3}[0-9]+");
    Pattern pattern = Pattern.compile("([+-]{0,2})[x]([\\^]" +"?(([+-]?)(\\d+)))?");
    Pattern pattern = "([+-]{0,2})sin[\\(]x[\\)]([\\^]([+-]?\\d+))?";
    Pattern pattern = "([+-]{0,2})[c][o][s][\\(]x[\\)]([\\^]([+-]?\\d+))?";

    分别得到了项的系数,\(x的指数,sin(x)的指数,cos(x)的指数\),然后存入我们的结构体中。最后通过上述求导公式对每个项进行求导,并且将相同系数的项合并。
    类图如下
    1613369-20190325163830684-712325602.jpg
    1613369-20190325164832903-1646333338.jpg

    可以看到其实第二次设计依旧没有秉持好的设计原则,虽然将不同的功能写在不同的方法里,但是没有实现类的分类,这里好的设计应该是sin(x),cos(x),x单独分类,然后进行求导,以及输出。然而这里混在了一起,导致main方法十分庞大,修改一个地方会导致很多方法都需要修改。因为代码之间的耦合度非常高,并且几乎所有的操作都是写在Poly里的静态方法,导致第三次作业又需要进行大规模的重构。

  • (3)第三次作业
    这次作业是我三次里认为还比较满意的一次作业,因为这次的题目比较复杂,因此我觉得不能再像前两次作业那样急于编写代码,因为这样会导致代码紊乱,最后难以找bug。因此在动手写代码之前,我仔仔细细地参照面相对象编程的思想,对第三次的题目进行了思考,先把类和接口设计好再进行动手编写代码,果然想清思路之后再下手,写起来快并且最后的bug也少了,代码思路非常清晰。

    简单分析一下这次的作业,这次的多项式求导不仅延续了之前的项的思路,还添加了嵌套求导的规则。即

      表达式 = 项{+项}项 = 因子*{因子}因子 = (表达式)|因子

    也就是类似\(sin(cos(x))\)这种嵌套的求导。我知道再延续以往的面相过程求导肯定是行不通的了。因此这次的设计对每一步进行了细致的类的划分。类图如下。
    1613369-20190325163937255-1342805664.jpg
    1613369-20190325164841319-1463899934.jpg

    下面来讲一下我的做法,首先我这次划分了很多个类,常数类、x项类、cos项类、sin项类、指数项类、加法类、乘法类这些类,这些类都实现了一个求导的接口,也就是说所有求导的对象都是另一个可求导的对象,比如说指数类里,指数的底数也是一个实现了求导的类,这样就很好地体现了分类的思想,并且在指数这个类里,我只需管指数函数是如何求导的,而不需要管其底数是什么,因为底数实现了求导接口,底数也会自动去调其覆写的求导方法去对底数进行求导。这样就使我们的程序显得很简单。
    这里的加法和乘法类就是指两个实现了求导接口的对象相乘进行求导,我们只需关心乘法的求导是怎么样的,而具体对象的求导,放在具体的对象的求导里去完成,这样就真正实现了低耦合的思想。

    具体的接口的代码如下:

    public interface Derive {
    public abstract Derive derive();//求导函数public abstract void print();
    }

    然后乘法里实现的求导接口的求导方法如下。

        public Derive derive() {ArrayList<Derive> addList = new ArrayList<Derive>();for (int i = 0; i < this.list.size(); i++) {/** 根据几个函数连乘的求导法则进行求导*  result = (ui' * (u1*u2*....un)/ui)的和*/ArrayList<Derive> multList = new ArrayList<Derive>();for (int j = 0; j < this.list.size(); j++) {if (i != j) {multList.add(this.list.get(j));}}multList.add(list.get(i).derive());Mult mult = new Mult(multList);/*** 这条multList 就是Add链中其中的一条**/addList.add(mult);}/*** 至次为止, addList是由好几个Mult类构成*/return new Add(addList);}

    我们可以看到,对于\(f(x) = h(x)*g(x)\)的求导,只需关心\(f(x)'=f(x)'g(x)+f(x)g(x)'\)即可,而不需关心\(f(x)'和g(x)'\)是什么,因为\(f(x)和g(x)\)都是已经实现了求导方法的对象,在他的类里会调用自己的求导方法进行递归求导。

    在明确了类的框架结构以后,我们再想办法对字符串进行处理,我一开始尝试原来的正则文法匹配的方法,但是发现自己不明白如何去产生上述的表达式里嵌套因子的方式,但是我发现,这个形式和我们之前学过的编译原理的递归下降分析法类似。于是我采用相同的思想先写了一个简单的词法分析小程序,然后构造了expr(),term(),factor()三个子程序来对字符串进行读取。这样就能实现程序的因子里嵌套表达式的形式了。下面举一个简单的表达式的例子。

    /*** 自顶向下的表达式子程序*/
    public static Derive expr() {/***  表达式 = 项 {+项}*/ArrayList<Derive> exprList = new ArrayList<Derive>();Derive term1 = term();exprList.add(term1);if (!checkExprTail()) {System.out.println("WRONG FORMAT!");System.exit(0);}while (sym.equals("Add") || sym.equals("Minus")) {headFlag = 1;term1 = term();exprList.add(term1);}if (!checkTail()) {err();}return new Add(exprList);
    }

    这样在后续的调试过程中我可以单独根据每种形式的求导来找问题,就能很快地发现是哪个求导过程发生了问题,简明扼要。

三:程序结构分析

  • 各类代码总规模:(statistic)

    SourceFileTotal LinesSource CodeSource CodeComment LinesBlank Lines
    Derive.java8431
    X.java10802
    Constant.java251906
    Sinx.java281846
    Cosx.java282035
    Mult.java5230139
    Add.java5440311
    Degree.java61361312
    Poly.java3903452124
    Total6585226076
  • 类的属性方法个数

    属性方法
    Derive.java02
    X.java02
    Constant.java13
    Sinx.java13
    Cosx.java13
    Mult.java12
    Add.java12
    Degree.java25
    Poly.java814

四:程序优缺点

  • 优点:

    1:将复杂的多项式求导分成诸多形式的类,每个类只需注意自己的求导形式,具体的求导规则由各个实现求导接口的类去完成。
    2:采用递归下降子程序法,使字符串的处理比较容易理解,并且不容易出错。
    3:使用了接口的思想,方便可扩展性。
    4:实现了高内聚低耦合的思想,使每个类和方法尽量能干的事情少,各自之间互不影响。
  • 缺点:

      1:在处理项的连乘的时候可能会出现爆栈的情况。2:没有做完备的可能发生异常的情况的统计与测试。3:单元测试不够完备,Main类的设计还过于冗杂。4:存在大量的if-else语句,不够精炼,存在代码复用比较严重。5:输出的时候如果嵌套层次太多,会导致大量的()产生,很难进行优化。

五:程序的Bug分析

  • 第一次

    第一次的bug在于没有处理指数或者系数过长可能抛出的异常,没有意识到助教和老师在作业指导书里的提示,这个问题在我理解了BigInteger类之后得到了较好的解决。并且在checkStyle风格检查的时候,没有按照规范要求的行数以及缩进。还存在表达式第一项如果为数字的话前面的符号的个数的问题。

  • 第二次

    • 第二次的第一个小bug是未考虑Scanner异常输入时抛出的异常会使程序crash,这里只需要在输入的地方加上try-catch的异常处理即可。
    • 第二个bug是在去除空格的时候没有吧制表符\t一起去除,没有重视空格space与制表符在ASCII码上不同的本质区别。
    • 第三个bug是在输出的时候,我是按照不管指数系数为不为0或1,全部将其按照\(a*x^b*sin(x)^c*cos(x)^d\)的格式输出,然后再对字符串进行处理,去掉"+1","1","^1"这些,但是忽略了完备性,如果一个指数恰好是 \(y^12\),那么去掉\(*1*\)之后就变成了\(y2\),这明显是错误的。错误的原因就是没有对类进行细分,如果按照第三次作业的方式对每个函数进行分类输出就会简单很多,可以分别判断系数、指数为0为1的情况,就可以省去大量的if-else并且保证程序的正确性。
  • 第三次作业

    • 没有考虑到输出的时候\(sin(2*x)\)这种错误的输出格式带来的问题。
    • 一开始没有做左右括号匹配的处理。

六:自我评价与寄语

通过第一个单元的学习,已经基本掌握了各种java类的用法,也理解了面向对象的设计思想。了解了继承与接口的原理。但是在使用上还存在不熟练的时候。希望在日后进行多线程学习之前,能够把java的基础打扎实,写出漂亮稳定的好程序。

转载于:https://www.cnblogs.com/somnus-chen/p/10591953.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言学习笔记(十二)-浮点指令

浮点数如何存储浮点寄存器浮点数指令浮点计算例子浮点高级运算CMOV移动指令 浮点数如何存储 浮点数的运算完全不同于整数&#xff0c;从寄存器到指令&#xff0c;都有一套独特的处理流程&#xff0c;浮点单元也称作x87 FPU。 现在看浮点数的表示方式&#xff0c;我们所知道的&a…

人工智能简述

人工智能研究的方向之一&#xff0c;是以所谓 “专家系统” 为代表的&#xff0c;用大量 “如果-就” &#xff08;If - Then&#xff09; 规则定义的&#xff0c;自上而下的思路。   人工神经网络 &#xff08; Artifical Neural Network&#xff09;&#xff0c;标志着另外…

Mockito 的使用

转自&#xff1a;Mockito 中文文档 ( 2.0.26 beta ) 转自&#xff1a;手把手教你 Mockito 的使用 参数匹配器 Argument Matcher(参数匹配器) Mockito通过equals()方法&#xff0c;来对方法参数进行验证。但是有时候我们需要更加灵活的参数需求&#xff0c;比如&#xff0c;匹配…

以SYSTEM用户运行CMD

在SCCM 经常会以NT AUTHOR\SYSTEM帐户操作。 安以下步骤可以以SYSTEM帐户打开一个CMD窗口。 1. 从微软网站下载PSTool。 2. 以管理员运行CMD&#xff0c;进入到解压的PSTool目录。 3. 运行psexec -i -s cmd.exe 4. 在新打开的CMD中运行whoami。 注&#xff1a;这个指令可以让你…

matlab cell

如果p为一个数&#xff0c;那么h(1)p,是没有问题的。 如果p为一个向量&#xff0c;那么h(1,:)p是没有问题的。 如果p是一个矩阵的话&#xff0c;上面的两种赋值方法都是会有错误的。 那么要如何处理呢&#xff1f; 这时就用到了cell数据类型了。cell的每个单元都可以存储任何数…

jboss 不适用内置日志_适用于孩子,父母和祖父母的JBoss HornetQ –第1章

jboss 不适用内置日志现在与HornetQ合作已经快4年了&#xff0c;我认为现在该分享我到目前为止所学的部分知识了。 这篇文章的主要目的不是重写官方文档 &#xff0c;而是以简单的方式阐明我们在PaddyPower中最常用的概念。 什么是HornetQ HornetQ是JMS实现。 JMS是一种面向…

Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon

客户端负载均衡&#xff1a;Spring Cloud Ribbon 一、负载均衡概念 负载均衡在系统架构中是一个非常重要&#xff0c;并且是不得不去实施的内容。因为负载均衡对系统的高可用性、 网络压力的缓解和处理能力的扩容的重要手段之一。通常所说的负载均衡指的是服务端负载均衡&#…

matlab cell,fix,floor,round取整的几种方式

ceil函数的作用是朝正无穷方向取整&#xff0c;即将m/n的结果向正无穷方向取整&#xff0c;如m/n3.12&#xff0c;则ceil(m/n)的结果为4。 类似的函数有如下几个&#xff1a; fix&#xff1a;朝零方向取整&#xff0c;如fix(-1.3)-1;fix(1.3)1; floor&#xff1a;朝负无穷方…

孤儿进程与僵尸进程[总结]

1、前言 之前在看《unix环境高级编程》第八章进程时候&#xff0c;提到孤儿进程和僵尸进程&#xff0c;一直对这两个概念比较模糊。今天被人问到什么是孤儿进程和僵尸进程&#xff0c;会带来什么问题&#xff0c;怎么解决&#xff0c;我只停留在概念上面&#xff0c;没有深入&a…

使用CloudForms实现云运营幸福感的3个步骤

本周宣布&#xff0c; Cloud Suite管理层的最新功能 CloudForms 4.2可供所有人使用。 产品增加了1800多种改进&#xff0c;令许多令人兴奋的新奇事物令人兴奋。 CloudForms是Cloud Suite产品的Cloud Management平台&#xff0c;使您能够跨区域联合部署它以进行集中管理操作&am…

leetcode 599. 两个列表的最小索引总和(Minimum Index Sum of Two Lists)

目录 题目描述&#xff1a;示例 1:示例 2:解法&#xff1a;题目描述&#xff1a; 假设Andy和Doris想在晚餐时选择一家餐厅&#xff0c;并且他们都有一个表示最喜爱餐厅的列表&#xff0c;每个餐厅的名字用字符串表示。 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如…

FAR,FRR,EER

FRR与FARFRR&#xff08;False Rejection Rate&#xff09;和FAR&#xff08;False Acceptance Rate&#xff09;是用来评估指纹识别算法性能的两个主要参数。FRR和FAR有时被用来评价一个指纹识别系统的性能&#xff0c;其实这并不贴切。指纹识别系统的性能除了受指纹算法的影响…

通过一段汇编,加深对寄存器ESP和EBP的理解

一直对寄存器ESP和EBP的概念总是有些混淆&#xff0c;查看定义ESP是栈顶指针&#xff0c;EBP是存取堆栈指针。还是不能很透彻理解。之后借于一段汇编代码&#xff0c;总算是对两者有个比较清晰的理解。下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码;假设执…

Redis和数据库 数据同步问题

Redis和数据库同步问题缓存充当数据库比如说Session这种访问非常频繁的数据&#xff0c;就适合采用这种方案&#xff1b;当然了&#xff0c;既然没有涉及到数据库&#xff0c;那么也就不会存在一致性问题&#xff1b;缓存充当数据库热点缓存读操作目前的读操作有个固定的套路&a…

matlab fspecial创建滤波算子

Fspecial函数用于创建预定义的滤波算子&#xff0c;其语法格式为&#xff1a;h fspecial(type) h fspecial(type,parameters,sigma)参数type制定算子类型&#xff0c;parameters指定相应的参数&#xff0c;具体格式为&#xff1a;typeaverage&#xff0c;为均值滤波&#xff…

hibernate jpa_JPA / Hibernate:基于版本的乐观并发控制

hibernate jpa本文是对Hibernate和JPA中基于版本的乐观并发控制的介绍。 这个概念已经很老了&#xff0c;上面已经写了很多东西&#xff0c;但是无论如何我都看到了它被重新发明&#xff0c;误解和滥用。 我在写它只是为了传播知识&#xff0c;并希望引起对并发控制和锁定的兴趣…

X86汇编快速入门

本文翻译自&#xff1a;http://www.cs.virginia.edu/~evans/cs216/guides/x86.html 本文描述基本的32位X86汇编语言的一个子集&#xff0c;其中涉及汇编语言的最核心部分&#xff0c;包括寄存器结构&#xff0c;数据表示&#xff0c;基本的操作指令&#xff08;包括数据传送指令…

Django(三)框架之第二篇

https://www.cnblogs.com/haiyan123/p/7717788.html 一、知识点回顾 1、MTV模型 model&#xff1a;模型&#xff0c;和数据库相关的 template&#xff1a;模板&#xff0c;存放html文件&#xff0c;模板语法&#xff08;目的是将变量如何巧妙的嵌入到HTML页面中&#xff09;。 …

使用GDB调试C库

用gdb调试程序时&#xff0c;一般的函数都可以step进去&#xff0c;可是C库函数却直接跳过了。 网上找了些资料&#xff0c;记录一下&#xff01; 1.安装C库的debug版本 [plain] view plaincopy print?sudo apt-get install libc6-dbg 安装完后&#xff0c;在/usr/lib目录下…

matlab imfilter对图像进行滤波

功能&#xff1a;对任意类型数组或多维图像进行滤波。 用法&#xff1a;B imfilter(A,H)    B imfilter(A,H,option1,option2,...)    或写作g imfilter(f, w, filtering_mode, boundary_options, size_options) 其中&#xff0c;f为输入图像&#xff0c;w为滤波掩模&…