我们小组的照片:
组员:黄上13061189 余帆13061155
结对编程的优缺点:优点:集思广益,结对编程可以相互讨论相互帮助,可以取长补短研究的更深入。
缺点:就是思维会相互制约,或者相互如果遇到两个人相同的缺点就不好办了。
余帆的优缺点:优点:1.思维活跃,灵敏,对c#和对象化编程非常的熟练。
2.学习的能力非常强,不会的东西可以自己去搜索和查询,而且学的特别快,掌握的非常灵活。
3.善于表达自己的想法,而且表达的非常准确无误。
4.善于发现问题的本质,极具洞察力。
5,乐于合作。
缺点:1.解决问题额能力有点慢。
我的优缺点:优点:1.动手能力强一些。
2.遇到不会的问题可以进行搜索和自学。
3.乐于合作。
缺点:1.对象化编程掌握的还是不是很娴熟。
2.不善于发现问题。
3.不善于表达。
我们的做法是将这个软件模块化,将函数封装起来。我们写了Calc,Create,Expression,core等类,其中Core是最重要的计算核心,他负责计算用户输入进来的四则运算算式。那么封装的意义又是什么呢?封装的意义在于保护或者防止代码被无意中破坏。这样可以保护数据不被其它函数修改。封装提供了一个有效的途径来保护数据不被意外破坏。相比于我们将数据在程序中定义为公用的(public)我们将他们定义为private会更好,这样可以数据就不会被其他函数调用。封装最大的好处就是让我们写的代码可移植性高,任何人都可以直接拿来用而不用去研究这个函数内部怎么运行的。于是我们用了我们把函数的属性列出来,用接口来来调用。对于使用者来说直接调用接口输入相关的数据就可以等到他慢想要的结果。对于开发者来说,就扣不变可以任意修改内部实现,也很方便。就好比我们制作了一个榨汁机,接口就是使用说明,用户按照说明方放入他门想要的水果,然后榨汁机自动把果汁弄出来。很方便。core给出如下:
1 class Core : CoreInterface 2 { 3 private int quantity; 4 private int maxrange; 5 private int maxsign; 6 private bool iffraction;//是否有分数 7 private bool ifbracket;//是否有括号 8 private bool ifmuldiv;//是否有乘除 9 private bool ifnegative;//是否有负数 10 private Random random = new Random(); 11 private String exercisefile; 12 private String answerfile; 13 //(未初始化) 14 15 public void setting(int n, int r,int s, bool fraction, bool bracket, bool muldiv, bool negative) 16 { 17 quantity = n; 18 maxrange = r; 19 maxsign = s; 20 iffraction = fraction; 21 ifbracket = bracket; 22 ifmuldiv = muldiv; 23 ifnegative = negative; 24 } 25 public bool create() 26 { 27 bool sign = true; 28 if (maxrange == 1 && iffraction == true) 29 { 30 sign = false; 31 return sign; 32 } 33 else if(maxsign <= 0) 34 { 35 sign = false; 36 return sign; 37 } 38 39 expressionList eL = new expressionList(quantity); 40 //表达式列表的类 41 int init = 0; 42 while (init < quantity) 43 { 44 Create c = new Create(quantity, maxrange, maxsign, iffraction, ifbracket, ifmuldiv, ifnegative, random); 45 //生成每一个表达式的类 46 expression e = c.Creation(); 47 //表达式类 48 if (eL.ifequal(e) == false)//判断不相等则添加 49 { 50 init++; 51 eL.Addexpression(e); 52 } 53 } 54 55 //Random random = new Random(); 56 57 eL.print(ifbracket); 58 59 //创建部分的Answers.txt输出 60 exercisefile = "Exercises.txt"; 61 StreamReader E1 = new StreamReader(exercisefile); 62 int total = 1; 63 Calc ca = new Calc(); 64 string output = null; 65 while (!E1.EndOfStream) 66 { 67 string f = E1.ReadLine(); 68 string formula = f.Substring((f.IndexOf(".") + 2), (f.Count() - f.IndexOf(".") - 2)); 69 string result = ca.calculate(formula); 70 if (output == null) 71 { 72 output = total.ToString(); 73 output += ". "; 74 } 75 else 76 { 77 output += total.ToString(); 78 output += ". "; 79 } 80 output += result; 81 output += "\r\n"; 82 total++; 83 } 84 FileStream Answerfile = new FileStream("Answers.txt", FileMode.Create); 85 StreamWriter A1 = new StreamWriter(Answerfile); 86 A1.Write(output); 87 A1.Close(); 88 return sign; 89 } 90 91 public bool checkout(string exer, string ans) 92 { 93 bool sign = true; 94 exercisefile = exer; 95 answerfile = ans; 96 if ((!File.Exists(@exercisefile)) || (!File.Exists(@answerfile))) 97 { 98 sign = false; 99 return sign; 100 } 101 StreamReader E1 = new StreamReader(exercisefile); 102 StreamReader A2 = new StreamReader(answerfile); 103 List<int> right = new List<int>(); 104 List<int> wrong = new List<int>(); 105 int total = 1; 106 Calc ca = new Calc(); 107 while ((!E1.EndOfStream)&&(!A2.EndOfStream)) 108 { 109 string f = E1.ReadLine(); 110 string formula = f.Substring((f.IndexOf(".") + 2), (f.Count() - f.IndexOf(".") - 2)); 111 string result = ca.calculate(formula); 112 113 string a = A2.ReadLine(); 114 string answer = a.Substring((a.IndexOf(".") + 2), (a.Count() - a.IndexOf(".") - 2)); 115 if (result == answer) 116 { 117 right.Add(total); 118 } 119 else 120 { 121 wrong.Add(total); 122 } 123 total++; 124 } 125 if (E1.EndOfStream && !A2.EndOfStream) 126 { 127 while (!A2.EndOfStream) 128 { 129 wrong.Add(total); 130 total++; 131 } 132 } 133 if (!E1.EndOfStream && A2.EndOfStream) 134 { 135 while (!E1.EndOfStream) 136 { 137 wrong.Add(total); 138 total++; 139 } 140 } 141 FileStream correctfile = new FileStream("Grade.txt", FileMode.Create); 142 StreamWriter C1 = new StreamWriter(correctfile); 143 string output = ("Correct: " + right.Count().ToString()); 144 int i; 145 if (right.Count() > 0) 146 { 147 output += (" ("); 148 for (i = 0; i < right.Count(); i++) 149 { 150 if (i != right.Count() - 1) 151 { 152 output += (right[i].ToString() + ", "); 153 } 154 else 155 { 156 output += (right[i].ToString() + ")\r\n"); 157 } 158 } 159 } 160 else 161 output += "\r\n"; 162 output += ("wrong: " + wrong.Count().ToString()); 163 if (wrong.Count() > 0) 164 { 165 output += (" ("); 166 for (i = 0; i < wrong.Count(); i++) 167 { 168 if (i != wrong.Count() - 1) 169 { 170 output += (wrong[i].ToString() + ", "); 171 } 172 else 173 { 174 output += (wrong[i].ToString() + ")\r\n"); 175 } 176 } 177 } 178 else 179 output += "\r\n"; 180 C1.Write(output); 181 C1.Close(); 182 return sign; 183 } 184 185 public string calculate(string f) 186 { 187 Calc ca = new Calc(); 188 string result = ca.calculate(f); 189 Console.WriteLine(result); 190 return result; 191 } 192 } 193 }
core的接口也给出如下:
public interface CoreInterface { void setting(int n, int r, int s, bool fraction, bool bracket, bool muldiv, bool negative); bool create(); bool checkout(string exer, string ans); string calculate(string f); }
由此可以看到封装和接口的好处。
三、原则分析:
1、Information Hiding(信息隐藏)
信息隐藏是结构化设计与面向对象设计的基础。在结构化中函数的概念和面向对象的封装思想都来源于信息隐藏。
David Parnas在1972年最早提出信息隐藏的观点。他在其论文中指出:代码模块应该采用定义良好的接口来封装,这些模块的内部结构应该是程序员的私有财产,外部是不可见的。 Fred Brooks在《人月神话》的20周年纪念版中承认了当时自己对Parnas的批评是错误的。他说道:“我确信信息隐藏--现在常常内建于面向对象的编程中--是唯一提高设计水平的途径”。 一些信息隐藏原则的应用: 1、 多层设计中的层与层之间加入接口层; 2 、所有类与类之间都通过接口类访问; 3 、类的所有数据成员都是private,所有访问都是通过访问函数实现的;
(以上内容来自博客http://blog.csdn.net/acloudhuang/article/details/6635230)
在写面向对象类型的软件或者程序的时候,要考虑到用户的使用问题,也要注意自己的代码保密问题,这个原则的使用能够很好的维护这个问题。
2、interface design(接口设计)
清晰是第一步,也是最重要的工作界面。什么算是有效使用你设计的一个接口?人们必须能够认识到它是什么,关心他们为什么会使用它,帮助他们理解接口相互作用,预测使用它会发生什么,然后成功地与之交互。
最 好的接口是根本没有,当我们能够直接操纵物体在我们的世界。因为这并不总是可能的,这个世界上有越来越多的信息和对象,我们创建接口来帮助我们与它们进行 交互。很容易添加更多的接口层,创建overly-wrought按钮,chrome,图形,选择,偏好,附件和其他繁琐的东西。这样我们最终操纵的UI 元素而不是重要的事情。相反,追求原始的目标直接操作,设计一个接口与尽可能少的足迹,识别尽可能多的自然人类的手势。理想情况下,界面非常轻,,用户直 接操作他们关注的对象。
3、loose coupling(松耦合)
松耦合的基本概念是:允许改变或者当问题发生在“电线的一端时”来避免影响到其他的端点。也就是说,改变或者供应者或者服务的问题不能影响到用户——或者用户的问题不应影响到供应者或者服务。
举 例来说,如果服务的新版本被推出的话,服务的用户就不必非要去修改,不必非要去改变路线,甚至不必经历停滞期----因为它(服务的新版本被推出)能清 楚地向他们显示出来。相对而言,如果服务的一个用户决定改变他们正在使用的供应者(可能有了一个更低价格的服务供选择),这不需要用户应用必须要被重新编 码,甚至是中断来做这样的改变。
程序组件之间的松散耦合可以通过使用增强的标准数据类型参数。通过定制数据类型或对象需要组件定义数据定义的知识。松散耦合的服务可以增强通过减少信息传递到一个服务的关键数据。
关于测试
测试用的是单元测试,除了赋值函数和更改属性的这种函数确实逻辑上说明正确就好之外,其他的函数也都测试一下其功能。举个简单的实例比如basic是用来计算最大公约数的,所以我的测试如下:
[TestMethod] public void TestMethod1() { No2.basic temp = new No2.basic(); int n = 3; int r = 9; int c = temp.gcd(n, r); Assert.IsTrue(c == 3); }
用了Assert来断言答案是什么,如果程序书输出的答案和我希望的一样,那就说明正确了。通过。其他的函数也这样测试,不一一赘述。
还有就是关于异常的检测。比如我输入数据是错误的,程序应该抛出异常,有如下实例:
[TestMethod] [ExpectedException(typeof(FormatException))] public void TestMethod3() { No2.Calc temp2 = new No2.Calc(); String formula = "1293857129481275823y57127834"; String re = temp2.calculate(formula); //Assert.IsTrue(re == "7"); }
其中用到了ExpectedException(typeof(FormatException))这个函数来检测是否程序抛出异常。换话说就是这个函数意思是预期异常,我故意输入一个错误的数据,那么程序就应该抛出异常信息,ExpectedException就是来检测是不是真的抛出异常了。
最后给出我们组的覆盖率。