面向对象先导课感想
收获和感想
作为一个虽然没有专门学过java但是早已经熟悉OOP程序设计方式,并使用 C#
有过大概几千行开发经验的学员,我的感想可能和大部分人有些不同。
java语言
说到java
和C#
,其实这是强类型语言里面两个最适合OOP设计的语言,而且两者之前有着相当高的语法相似度(毕竟都是满满的C系语言风格)。而且都是在整个项目中指定一个入口点类,然后从 static void main
函数入口,就像这样(简单的A+B问题的实现):
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace APlusB {
class Program {
static void Main(string[] args) {
string[] numbers = Console.ReadLine().Split(new char[] { ' ' });
int a = int.Parse(numbers[0]);
int b = int.Parse(numbers[1]);
Console.WriteLine(a + b);
}
}
}
Java
package aplusb;
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
int b = sc.nextInt();
System.out.println(a + b);
}
}
有一个之前遇到过的坑,就是java的异常处理机制。在java中,如果一个函数(方法)需要抛出异常的话,是必须在当前函数(方法)处进行声明的。同时外部调用这个函数(方法)的部分也必须使用异常处理语句包装起来(即必须放进 try { }
中)。
这样做的一个很大的好处是强迫开发者完全将所有的异常保持在一个可控的状态,即每一层对于内层的异常都会做好完全的处理。不过缺点也很明显——语法太烦了,尤其是当异常状态很多很杂时候,外部调用可以变得非常繁琐。
而C#
中则完全不需要这些,抛出异常无需声明,也可以随意的使用可能有异常的函数(方法)(不过由于乱抛异常导致的程序报错结果也得自己处理。)
基于java的OOP
还记得在第一次正式讲OOP时,java OOP很重要的一个原则就是不允许任何变量直接暴露给用户。
这一点的确没有错,变量直接暴露给用户会导致部分数据失去控制,从而导致整个对象模型内部紊乱。可是很多时候还是需要这样子的变量的,尤其是频繁访问的数据,不停地 get
, set
总让人感到非常的不舒服,语法也会变得很臃肿。
对于java而言,这的确是无解的,对于频繁直接改动的值总还是不得不get
, set
。
而在C#中,则有个叫做属性的东西,可以很好的解决这一问题,就像这样
protected int p_val;
public int val {
get {
return this.p_val;
}
set {
this.p_val = value;
}
}
程序调用时,就可以像调用一个变量一样的调用val
了,无需括号,无需get
, set
,语法十分优雅。
不仅如此,属性还可以完美支持很多更加强大的功能
- 只读属性
protected int p_val;
public int val {
get {
return this.p_val;
}
}
这样一来val
就只可以读出不可以更改了
- 将复杂的数据维护动作简单封装起来
其实,属性也是基于get
和set
的模式的,只不过将其按照赋值和取值的两种动作分别进行了封装。get
,set
内部本质还是一个函数,可以执行复杂任务的函数。
同时,java和c#都作为严格的强类型OOP语言,很多机制(例如:强类型的继承、接口、反射、函数的重载等)也都是完全具备的(相比之下,弱类型则不需要接口和函数重载之类的东西,像php这样的语言连反射也都是完全内置化的,而且弱类型语言普遍有个叫做eval的函数,可以基本上取代掉传统的反射类)。就语法舒适程度而言,个人还是更支持c#一些。不过java有个至今无可替代的优势——完美的跨平台支持(java的虚拟机遍布各个平台,即涉及到各个平台底层的东西java早已替编程者实现好了),且java的部分特性决定了java更适合作为OOP初学者语言。
总之这两个语言,各有各的好处,还有很多的东西值得我去进一步研究和学习。以及,作为一个合格的 IT learner,而不是廉价的劳动力码农,眼光也绝对不可以局限于语言本身,而应该是语言之上更深层次的东西。
对于课程设计的建议
相比于之前的数据结构与程序设计课程,面向对象课程存在一个比较明显的问题——由于很多时候只有大致的需求而没有很明确的输入输出或交互要求,所以很难做成类似OJ那样的自动化评测,所以很多时候还是只能依赖于人手工评测,这样非常的费时费力。
其实个人感觉,做出来OJ模式的测评本身并不是难事。但是带来的问题是,如果完全采用黑盒测试,则没有办法保证学生采用了所需的面向对象设计模式。
我个人的建议是:将人工查看代码和黑盒测试相结合
人工查看可以一定程度上保证设计模式按照要求。同时黑盒测试也可以真正更加方便和可感地衡量一个程序的真实性能和不足,同时大大提高测试效率。
关于课程本身,我想说的就是如何平衡一下Java语言的教学和真正面向对象知识的教学,让无java基础的人不至于完全掉队,也让有一定基础的人不被太多拖慢进度。
关于互测
由于有些东西可能真的还是难以全面采用黑盒测试,所以不得已只能采用互测的方式。就目前来看,当前的互测方式有明显不公平不合理之处。
- 由于采用的是每个人随机分配一名被hack者的原因,所以这直接导致每个人能hack的空间非常有限。说的更直接点,如果你很幸运抽到了一个代码满是bug的程序,那你的分数可以非常高;如果你hack的人程序结构相当严密,设计优化程度相当可以,那这一点上你基本没法指望。
- 同时也是由于上面一点,从被hack者的角度而言,也存在极大的漏网空间,很大程度上也取决于运气。同一份程序,如果你的hacker是个技术了得,你大概会很惨;如果他敷衍了事或者水平欠佳,那你得分也一样不会低,甚至完全不低于那些真正把程序写的很棒的人。
这样的模式,有很多的弊病:
- 由于存在如此巨大的不公平空间,所以很难真正激发同学之间互相纠正错误的欲望,根本无法达到类似
Codeforces
那样的互相纠正的作用。相反,这样的措施一旦限制稍有失误,便可能导致严重的恶性竞争(甚至是一些不正当线下交易)。 - 从学生的未来发展来看,这样的措施会导致很多该纠正的bug和系统设计错误无法被及时纠正。一旦少数当时作为漏网之鱼的学生带着这种错误的设计方式进入了其他单位,那么对学生本人和那个单位而言都是极为不利的。
我的建议:
- 在
Codeforces
(Codeforces 网站链接) 中,一旦有人成功hack了别人的一份程序,那么终测的时候,所有之前获得Accepted
状态的程序都会被所有这些成功hack别人的数据重新测试一下。也就是说实际上即便你直接hack的人只有一个或者几个,但实际上作用到的人是所有人(很多时候,很多错误,都是非常具有共性的,一个hack点常常可以卡掉非常多份的程序)。建议这样的广泛测试机制可以纳入考核,有助于大幅度提高公平性和教学的质量。 - 在
Codeforces
中, 每一份程序都是被挂出来让大家一起来hack的。这样当看得人多了,缺陷自然会很快全部曝光出来。建议更多的程序让大家开放hack,也可以让真正有足够能力hack的人能发挥应有的作用。(不过这样一来就必须做好严密的反作弊反抄袭系统,为了杜绝由于代码公开而导致的抄袭现象。不过也有一种办法就是等到ddl之后,所有人停止提交,这时候再开放hack。)
总之,个人觉得,互测机制本身是个初衷很好的机制。但是里面的相关制度和模式还是应该有进一步完善的空间。最后期待半年后我们的OO课程更加科学合理。