C#真的就比Java节省成本吗?

2010-03-21

按照微软的说法,C#比Java节省成本,真的这样吗?那就分析一下宣传中C#比Java省成本的那些方面。

第一,C#的Web开发比Java方便。

大部分C#开发人员所使用的,只是ASP.Net的功能中的一少部分,实际上ASP.Net很庞大,标签、Session等功能,ASP.Net里都有,甚至比JSP还要庞大,老手可以凭借这些拉开和新手之间的距离。如果不用这些功能的话,就会留下大量冗长难以维护的代码。但是在JSP开发中,为了令代码清晰可维护,标签、Sevlet等功能是必须要用的。使用C#做Web开发,节省成本的做法大多是在页面上编写大量的代码,以这种方法来让工资要求低的新手来加入开发,JSP开发过程中当然也可以这么做,但这是以牺牲项目的可维护性,甚至将压力转稼给未来维护工作为代价的。以使用了类似JSP功能中的少部分和使用了JSP功能的大部分的方法比,这只是个宣传技巧罢了。

第二,C#编程语言比Java简单。

C#基本上就是C++的虚拟机版,而且在面向对象方面的技巧比C++还多,并且继承了Delphi中的部分语法和lambda等功能,学习难度很大,C++的语法能写满很厚的一本书,C#能写得更厚,虽然《Big Java》等介绍Java的书籍也同样厚,但只有前面1/4的内容是介绍Java的语法,後面的内容则是有关Swing和Java类库等内容,如果不使用的话完全可以不去看,Java的类库有电子文档,查找起来很方便。但是无论任何编程语言,语法级的东西查找起来就不会那么顺利了。

第三,C#的开发环境比Java完备。

暂不提背景深厚且有很多商业软件支持的Eclipse,单是小巧且开源得非常彻底的Netbeans,其功能涵盖了Java开发的大部分方面,而且对於多平台的支持也很成熟,没有被限制在某个平台上的情况。

虽然C#在架构上相对於Java并没有成本优势,那么软件公司的Java项目中耗费成本的地方都在哪些方面呢?

一,Java程序员接触国外项目的机会多。

虽然外企门槛奇高,甚至IETLS比技术还重要,但是国外特别是美国Java应用得非常广泛,会Java的人进入外企拿相对的高薪的可能性还是比较高,这一点带动了Java项目的成本消耗。

二,必须使用框架。

虽然使用框架会让项目结构更加清晰,但是Java圈子里使用框架俨然成了一种约定俗成的规则,不使用框架或使用过於简单的框架的话往往要在出现问题的时候承担责任。以至於使用框架被当成提升价值的手段,甚至出现了过度使用框架,加大开发难度的情况。

三,Java项目的高需求量拉高价格。

由於Java在国外应用时间比较长,国外的程序员对Java项目的掌控经验不断通过演讲和技术文章等形式进入国内,而且,因此Java项目的平均质量比较成熟,所以更加抢手,以至於Java项目的价格水涨船高。

四,J2EE平台对底层协议封装得比较彻底。

如果在J2EE平台上进行项目开发,基本上不需要接触CORBA、SOAP和HTTP等底层协议,以至於某些开发人员的能力被局限於J2EE平台上,在需要使用某些底层功能的时候往往会借助於其他中间件,从而增加开发的成本。

所以Java开发人员目前需要做的是,提高个人能力,锻炼“独立作战”的能力,改变他人对Java开发的“人海战术”、“炮灰战术”等印象是目前最重要的。

Gtk还是Qt?

2009-12-27

最近想写一些图形界面的程序,但是不知道用哪种图形界面库比较好。

Qt优点:API整齐,界面好看,组件灵活,移植方便。缺点:开发使用C++,本来就用着Java,再加上个C++,真受不了。

Gtk优点:简单,开发使用C语言。缺点:界面难看,严重依赖重量级图形库(例如xlib)。

两难啊。

用maven编译ibatis

2009-12-26

根据ibatis包中的jar-dependencies.txt中的描述,编译ibatis需要如下套件:

CGLIB 2.0 (http://cglib.sf.net)
DBCP 1.1 (http://jakarta.apache.org/commons/dbcp/)
OSCache 2.0.1 (http://www.opensymphony.com/oscache/)
Commons Logging (http://jakarta.apache.org/commons/)
Log4J 1.2.8 (http://logging.apache.org/log4j/docs/)

但是在maven的官方套件库里除了log4j以外其余的都没找到,大多是因为相应的版本没有收录,所以只能调整一下版本,在这里调整版本的目的只是为了能够通过编译,在运行环境里还是用文档里推荐的版本比较好。最好的方法是自己从官方网站上下载合适的版本并且用mvn install安装到本地。

还有就是要用JDK5编译,千万不要用JDK6,因为JRE6里的一些接口已经较JRE5做了一些改动,如果某个源代码文件使用了这样的接口的话编译时就会出错。

编译时候使用的脚本(pom.xml)如下:

Xml代码

1.
3. 4.0.0
4. org.apache.ibatis
5. iBatis
6. jar
7. 2.3.4.726
8. iBatis
9. http://ibatis.apache.org
10.
11.
12.
13. junit
14. junit
15. 3.8.1
16. test
17.
18.
19. cglib
20. cglib
21. 2.0.2
22.
23.
24. commons-dbcp
25. commons-dbcp
26. 1.2.2
27.
28.
29. opensymphony
30. oscache
31. 2.0.2
32.
33.
34. javax.transaction
35. jta
36. 1.1
37.
38.
39. commons-logging
40. commons-logging
41. 1.1.1
42.
43.
44. log4j
45. log4j
46. 1.2.8
47.
48.
49.
50.
51.
52.
53. org.apache.maven.plugins
54. maven-compiler-plugin
55.
56. 1.5
57. 1.5
58.
59.
60.
61.
62.

答复: 产线管理系统,如何做架构?

2009-12-26

http://www.javaeye.com/post/1058669

halida 写道

我们企业用 delphi+oracle做产线管理系统,作业方式就是给我们的产品(手机)贴条码,然后通过刷条码进系统来管控流程。有时候要调用外部程序,有时候要打印(传文档到LPT端口)。因为windows费用的问题,部分电脑采用linux+wine的方式来运行系统。

现在我们考虑重新开发系统,因为Delphi渐渐不支持了,以及因为效率的原因,要换到3层的架构。我们主管的想法是使用.net开发。我不知道.net的界面是否可以支持跨平台。

我个人因为兴趣原因,想用python+pyqt+django+salalchemy来开发系统,但是因为自己的实力不够,所以不好来推这样的架构,我的想法是客户端用pyqt,采用xmlrpc连接业务逻辑服务器,同时运行一个web服务器来显示查询页面(也可以整合到客户端中),DB端用 sqlalchemy连到oracle。

请问各位觉得我的想法如何,有什么更好的实现方式(实在不想用.net开发,但是考虑到可维护性,不知道.net能够撑多久)?

列一下需求:
3层架构
夸平台
能够运行本地程序,做一些交互
方便分布(产线电脑,有些是局域网,只能访问特定的服务器)
方便开发(个人建议能够接近Literate Programming,或者脚本式开发)
实时性好,产线作业员可等不了几秒。

我们的开发team为3人,我是主程序员,可以在架构上面说得上话。只要我能够开发出一个原型,并作出分析报告,采用我的架构应该没有问题。业务逻辑都是现成的,只要按照逻辑重新coding就好了。
我对架构的方面完全没有经验,请问应该如何去学习相关的知识呢?

我的回复:

我做过类似的系统,这样的系统功能跨度比较大,如果可能的话建议只用Java写。

不知道“客户端”机器配置怎样,如果配置相当好的话和服务器端可以用ActiveMQ做消息通信,如果配置差一点的话再考虑用PyQt什么的来调服务器上的Web Service。如果时间充足的话可以自定义一套消息协议,在服务器端准备一个线程池接收消息,这套消息系统恐怕需要一个人专门维护。服务器端越简单越来越好,Tomcat足够了,最好连Tomcat也别用,ActiveMQ可以单独启动,自己做的消息系统就更不用说了。

至于打印什么的,做好了消息通讯之后一个礼拜的时间就能完成。

做这样的系统最佳搭配莫过于tuxedo了,不过报出价来你们老板肯定不接受。

你给的信息有限,不知道“客户端”的软件需要输入一些什么样的内容。所以一些细节上没法帮你参谋。这样的系统干系重大,所以多花一些时间做得精细一些很有必要。

祝好运。

创建JNDI Binding

2009-12-26

开发J2EE程序时候,最让人头疼的莫过於各种各样的JNDI Binding,用得最多的一个类型要算DataSource,某些应用服务器上还有邮件服务、目录服务等等。

J2EE程序使用起来十分简单灵活,把服务器安装好然後把J2EE程序上传上去就可以了,比起PHP程序来要简单得多,但是在开发和测试时就要遇到各类名目繁多的概念和封装。一些非EJB项目,例如用Spring组织的项目,为了提高速度往往需要用一些静态变量,但是这些静态变量在程序装载和卸除时经常会出现一些奇怪的问题。

解决这些问题颇费周折,最好的方法莫过於把业务逻辑拿到应用服务器之外编写和测试,但是应用服务器一般都自带大量强大的JNDI资源,废弃不用的话产品会丢失不少卖点,将其模拟下来是最好的方法。一个项目甚至项目里的每个模块,在开发前期做好测试计划都是很有必要的,但这个话题太大了,这里写不了,我这里只写一些关於自己创建JNDI Binding的内容。

自己创建JNDI Binding需要建一大堆模型,至少需要InitialContextFactoryBuilder、InitialContextFactory和Context这三个,不可谓不复杂,但是其机制并不繁琐,只是想得周全一些罢了,例子代码如下:

Java代码

1. package testjndi;
2.
3. import java.util.Hashtable;
4. import javax.naming.Binding;
5. import javax.naming.Context;
6. import javax.naming.Name;
7. import javax.naming.NameClassPair;
8. import javax.naming.NameParser;
9. import javax.naming.NamingEnumeration;
10. import javax.naming.NamingException;
11. import javax.naming.spi.InitialContextFactory;
12. import javax.naming.spi.InitialContextFactoryBuilder;
13. import javax.naming.spi.NamingManager;
14.
15. /**
16. *
17. * @author
18. */
19. public class CustomizedInitialContextFactoryBuilder
20. implements InitialContextFactoryBuilder {
21.
22. public static void initialize() {
23.
24. try {
25.
26. // 覆盖原先的设置
27. NamingManager.setInitialContextFactoryBuilder(
28. new CustomizedInitialContextFactoryBuilder());
29. }catch(Exception e) {
30. e.printStackTrace(System.err);
31. }
32. }
33.
34. public InitialContextFactory createInitialContextFactory
35. (Hashtable environment) throws NamingException {
36.
37. return new MyCustomizedInitialContextFactory();
38. }
39.
40. private class MyCustomizedInitialContextFactory
41. implements InitialContextFactory {
42.
43. public Context getInitialContext(Hashtable environment)
44. throws NamingException {
45.
46. CustomizedContext customizedContext
47. = new CustomizedContext();
48.
49. return customizedContext;
50. }
51. }
52.
53. private class CustomizedContext implements Context {
54.
55. public Object addToEnvironment(String propName, Object propVal)
56. throws NamingException {
57.
58. throw new UnsupportedOperationException(“Not supported yet.”);
59. }
60.
61. public void bind(Name name, Object obj) throws NamingException {
62. throw new UnsupportedOperationException(“Not supported yet.”);
63. }
64.
65. public void bind(String name, Object obj) throws NamingException {
66. throw new UnsupportedOperationException(“Not supported yet.”);
67. }
68.
69. public void close() throws NamingException {
70. throw new UnsupportedOperationException(“Not supported yet.”);
71. }
72.
73. public Name composeName(Name name, Name prefix) throws NamingException {
74. throw new UnsupportedOperationException(“Not supported yet.”);
75. }
76.
77. public String composeName(String name, String prefix)
78. throws NamingException {
79.
80. throw new UnsupportedOperationException(“Not supported yet.”);
81. }
82.
83. public Context createSubcontext(Name name) throws NamingException {
84. throw new UnsupportedOperationException(“Not supported yet.”);
85. }
86.
87. public Context createSubcontext(String name) throws NamingException {
88. throw new UnsupportedOperationException(“Not supported yet.”);
89. }
90.
91. public void destroySubcontext(Name name) throws NamingException {
92. throw new UnsupportedOperationException(“Not supported yet.”);
93. }
94.
95. public void destroySubcontext(String name) throws NamingException {
96. throw new UnsupportedOperationException(“Not supported yet.”);
97. }
98.
99. public Hashtable getEnvironment() throws NamingException {
100. throw new UnsupportedOperationException(“Not supported yet.”);
101. }
102.
103. public String getNameInNamespace() throws NamingException {
104. throw new UnsupportedOperationException(“Not supported yet.”);
105. }
106.
107. public NameParser getNameParser(Name name) throws NamingException {
108. throw new UnsupportedOperationException(“Not supported yet.”);
109. }
110.
111. public NameParser getNameParser(String name) throws NamingException {
112. throw new UnsupportedOperationException(“Not supported yet.”);
113. }
114.
115. public NamingEnumeration list(Name name)
116. throws NamingException {
117.
118. throw new UnsupportedOperationException(“Not supported yet.”);
119. }
120.
121. public NamingEnumeration list(String name)
122. throws NamingException {
123.
124. throw new UnsupportedOperationException(“Not supported yet.”);
125. }
126.
127. public NamingEnumeration listBindings(Name name)
128. throws NamingException {
129.
130. throw new UnsupportedOperationException(“Not supported yet.”);
131. }
132.
133. public NamingEnumeration listBindings(String name)
134. throws NamingException {
135.
136. throw new UnsupportedOperationException(“Not supported yet.”);
137. }
138.
139. public Object lookup(Name name) throws NamingException {
140. throw new UnsupportedOperationException(“Not supported yet.”);
141. }
142.
143. public Object lookup(String name) throws NamingException {
144.
145. // 在这里将JNDI名帮定到实际的内容上
146. if (name == null) {
147. throw new NamingException(“Name must not be null.”);
148. }else if(name.equals(“Key”)) {
149. return “Value”;
150. }else {
151. throw new NamingException(“Name \”" + name + “\” not found.”);
152. }
153. }
154.
155. public Object lookupLink(Name name) throws NamingException {
156. throw new UnsupportedOperationException(“Not supported yet.”);
157. }
158.
159. public Object lookupLink(String name) throws NamingException {
160. throw new UnsupportedOperationException(“Not supported yet.”);
161. }
162.
163. public void rebind(Name name, Object obj) throws NamingException {
164. throw new UnsupportedOperationException(“Not supported yet.”);
165. }
166.
167. public void rebind(String name, Object obj) throws NamingException {
168. throw new UnsupportedOperationException(“Not supported yet.”);
169. }
170.
171. public Object removeFromEnvironment(String propName)
172. throws NamingException {
173.
174. throw new UnsupportedOperationException(“Not supported yet.”);
175. }
176.
177. public void rename(Name oldName, Name newName) throws NamingException {
178. throw new UnsupportedOperationException(“Not supported yet.”);
179. }
180.
181. public void rename(String oldName, String newName)
182. throws NamingException {
183.
184. throw new UnsupportedOperationException(“Not supported yet.”);
185. }
186.
187. public void unbind(Name name) throws NamingException {
188. throw new UnsupportedOperationException(“Not supported yet.”);
189. }
190.
191. public void unbind(String name) throws NamingException {
192. throw new UnsupportedOperationException(“Not supported yet.”);
193. }
194.
195.
196. }
197. }

package testjndi;

import java.util.Hashtable;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.InitialContextFactoryBuilder;
import javax.naming.spi.NamingManager;

/**
*
* @author
*/
public class CustomizedInitialContextFactoryBuilder
implements InitialContextFactoryBuilder {

public static void initialize() {

try {

// 覆盖原先的设置
NamingManager.setInitialContextFactoryBuilder(
new CustomizedInitialContextFactoryBuilder());
}catch(Exception e) {
e.printStackTrace(System.err);
}
}

public InitialContextFactory createInitialContextFactory
(Hashtable environment) throws NamingException {

return new MyCustomizedInitialContextFactory();
}

private class MyCustomizedInitialContextFactory
implements InitialContextFactory {

public Context getInitialContext(Hashtable environment)
throws NamingException {

CustomizedContext customizedContext
= new CustomizedContext();

return customizedContext;
}
}

private class CustomizedContext implements Context {

public Object addToEnvironment(String propName, Object propVal)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public void bind(Name name, Object obj) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void bind(String name, Object obj) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void close() throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Name composeName(Name name, Name prefix) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public String composeName(String name, String prefix)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public Context createSubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Context createSubcontext(String name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void destroySubcontext(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void destroySubcontext(String name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Hashtable getEnvironment() throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public String getNameInNamespace() throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public NameParser getNameParser(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public NameParser getNameParser(String name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public NamingEnumeration list(Name name)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public NamingEnumeration list(String name)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public NamingEnumeration listBindings(Name name)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public NamingEnumeration listBindings(String name)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public Object lookup(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Object lookup(String name) throws NamingException {

// 在这里将JNDI名帮定到实际的内容上
if (name == null) {
throw new NamingException(“Name must not be null.”);
}else if(name.equals(“Key”)) {
return “Value”;
}else {
throw new NamingException(“Name \”" + name + “\” not found.”);
}
}

public Object lookupLink(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Object lookupLink(String name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void rebind(Name name, Object obj) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void rebind(String name, Object obj) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public Object removeFromEnvironment(String propName)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public void rename(Name oldName, Name newName) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void rename(String oldName, String newName)
throws NamingException {

throw new UnsupportedOperationException(“Not supported yet.”);
}

public void unbind(Name name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

public void unbind(String name) throws NamingException {
throw new UnsupportedOperationException(“Not supported yet.”);
}

}
}

最後用一段代码验证一下结果:

Java代码

1. public class Main {
2.
3. /**
4. * @param args the command line arguments
5. */
6. public static void main(String[] args) {
7.
8. // 启动JNDI资源
9. CustomizedInitialContextFactoryBuilder.initialize();
10.
11. try {
12. InitialContext initialContext = new InitialContext();
13. System.out.println(initialContext.lookup(“Key”));
14. }catch(Exception e) {
15. e.printStackTrace(System.err);
16. }
17. }
18.
19.
20. }

public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {

// 启动JNDI资源
CustomizedInitialContextFactoryBuilder.initialize();

try {
InitialContext initialContext = new InitialContext();
System.out.println(initialContext.lookup(“Key”));
}catch(Exception e) {
e.printStackTrace(System.err);
}
}

}

打印出”Value”,结果正确。

对软件开发流程的感觉

2009-12-26

最近软件开发圈子内最流行的似乎已经不是某项技术,最热门的话题仿佛都是关於软件开发流程的,大堆的“SCRUM”、“敏捷”、“XX驱动开发”等等新书搬上了书架,似乎要改朝换代的样子。infoq、CSDN等网站也闹个不停,连登专题,国内XX之父、XX教主、XX第一人等等大角色纷纷登台亮相,一时间豪杰辈出,绚丽非常。

如果是未经历过炒作漩涡的新手看了,往往会被唬住,但是看得多了,经历得多了逐渐就有了疑问,这些真的像广告里说的那么有效吗?

目前风头最劲的莫过於敏捷(Agile)了,介绍“敏捷开发”的书籍杂志,从原创到翻译,再到影印,能排满一面墙,广告里一个比一个吹得邪乎,但通过观察一些想借实行敏捷开发改善流程的团队发现,效果并不像吹嘘的那样好,项目往往仍然混乱,冲突没有削减的迹象,对敏捷的效果似乎全无感觉,可仍然视 “敏捷”为救命稻草,甚至出现了“不是敏捷不好,是其他人做得不好”等等抱怨和指责。不过冷静地分析一下,多少能看出问题出在哪儿。

“敏捷开发”最吸引人的,莫过於减少浪费,但是我们首先要看到的是“敏捷开发”以及“SCRUM”、“XX驱动开发”等等开发“流程”,实际上只是个改进的方法或一些补充,离开了团队的原有风格它就什么也不是。比如要减少浪费,我们首先要对现有花费进行核算,否则我们如何知道其是否减少了浪费,以及减掉的是不是浪费?我们想要改进交流,首先我们要将思路整理一下,我们在什么时候需要开会?提出的问题如何处理?讨论的内容如何处理?会後谁来总结?总结过後应该怎么办?有人改动了源代码,我们如何知道改动是否合理?用什么标准去衡量?这些问题如果尚未解决,再敏捷有什么意义?白花了冤枉钱,甚至带来更大的混乱。做项目如同烹煮菜肴,用天然气和电炉代替烧柴火显然更加经济,要改进的就是这些方面,切不可为了消除浪费而去削减购买食材的花销,後果可想而知。

虽然我没有十足的证据,但是感觉,新的概念出现的背後,往往都有咨询公司的影子,一个团队想要引进如上文所述的那些新的概念,往往要求助於咨询公司,此等玛门之事如没有充分的把握和强烈的呼声,还是不要轻易涉足的好,倒不如花在一些公司管理课程和商务礼仪培训上更加实用些,因为真正的成功经验和合理的组织案例,在团队里往往视为珍宝,至少在国内是不可能轻易得到的。

现在手头珍藏着几本软件工程方面的书,当初看过後感觉不仅上了一堂工程理论课,更是上了一堂历史课,比如系统集成和测试,原先是为了装配大型机械设计的,配置管理则是起源於美国海军用来管理战斗机的工作手册,在二战後逐渐进入了各个行业,这些是那些浅显的概念所无法比拟的。

不仅在IT行业,其他行业中也有不少人也在羡慕欧美经济发达国家的同行,职位相同,但收入却多很多,但是咱们不能太看贱自己,一味地追风逐影,最後终究会害了自己。

钱钟书先生的一句话讲得很有道理:“中国真厉害,外国的好东西,来一样毁一样”。诸位不可不察。

开始blog了

2009-12-26

计划从今天开始好好地写下blog。

Blog begins.

2009-12-26

I will begin my blog here.

Hello world!

2009-12-26

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!


Follow

Get every new post delivered to your Inbox.