通过一个简单控制台的实现来漫谈软件工程基本概念
2013-01-26 16:14
435 查看
之前我曾写过同样内容的文章并发表在我的CSDN博客。我让我的同学过目,但他似乎并没有看懂。然后我就卸下来了。因为我感觉我的文笔确实不够好,很多道理阐述的过于冗杂,让别人无所适从。在此,又重写之,估计已经时隔一年了。
好了闲话不说,首先阐述我写这篇博客的目的吧。我想采用JAVA实现一个简单的控制台来浅谈关于软件工程方面的相关技术和概念。也许这些都是次要的,我的终极目标是想通过一个简单的不能再简单的实例,来一步步地体验软件开发的全过程,并掌握软件开发里的基本交流语言。并且,在此基础上,让那些对自己技术实力没有过多自信的人找到合适的切入点,让他们自己能够真正领悟到:软件开发是个极度包容的过程,几乎任何人都可以参与。他们所要学习的,就是在这个过程中,如何扬长避短,并把自己的技术短板限制在可控的范围内,以求自身真正地融入团队中。
任何软件开发事业,首先需要明确需求。我不想过多的谈论需求的重要性,它可能会占用过多的篇幅。我只想说,在需求明确之前,永远不要进行下一步。你要在动笔写代码之前就要搞清楚你所要实现的东西是什么样子,而不是在开发过程中逐渐熟悉它是什么样子。
下面述说我的需求:
即实现一个控制台。所谓控制台,是指类似于windows系统的cmd的东西。显然它的功能包括:1.能够输出字符串到屏幕上;2.能够接受用户输入从而获取字符串。
在这里我想通过JAVA实现。用什么语言无所谓,关键是阐述的道理都是一致的。java是我最熟悉的语言,我用它已经三年了。
然后等一下,我在这里想要多加一个需求。我想要阐述一下有关组件的概念。我希望我所实现的控制台,它是一个组件,而不仅仅是一个应用程序。所谓组件,就是能够提供一个或一系列相关功能的工具包。在java里,它可能是一个类,也可能是几个类联合起来实现一个功能。总之,它是可以被其他更高层的程序利用的。所以,我在这里添加一个需求,也许与最终的用户无关,但是对于开发人员却是必要的:即作为一个组件,为其他应用程序提供可访问的接口。事实上,软件开发就是某种程度的组件组合。
好吧,总结一下三个需求:
1.能够输出字符串到屏幕上;
2.能够接受用户输入从而获取字符串。
3.作为一个组件,为其他应用程序提供可访问的接口。
明确需求过后,需要进一步细化需求,或者可以称之为大家熟悉的案例。那我就随意细化之了。为了节省篇幅,我就并不写的很详细了。平常大家开发时,可以口头交流细节,但最好还是要文档化。在这里,我的控制台跟大家见过的差不多。就是可以输出字符串,光标总是在最后面,接受字符串一直到按下enter键才有反应等等。事实上这些不重要,我的需求的三个功能已经很明确了,它们只不过是不够详细罢了。详细和明确并不对等。
接下来可以真正考虑去实现了。首先我定义一个接口,称之ConsoleInterface。其具体代码如下:
另外,需要说明的是,这个准则不要过分滥用。亦即,不要遇到什么就尝试去定义一个接口。如果你的功能你能够一气呵成地完成,而且根本不用担心日后的维护时,应该放弃先定义接口的方式。毕竟,接口与实现分离是一种曲线救国的方式,是为了长久性的考虑或者是不得已而为之的结果。这里,我并不打算让自己采用接口的方式,毕竟只是个小项目,而且我对自己完成它是充满信心的。好了,故而我生命一个类,它有个更简洁优雅的名称,如下:
这个时候不要一筹莫展,先实现一部分再说。首先,不去考虑有关图形界面的部分,它可能是一个JTextArea,也可能是一个JPanel或者别的是什么,但先不去考虑这么多了,我们只需假设它具有下面的能力即可:
1.如果给它一个char数组,它能够立刻显示其中的内容。
2.如果用户输入一段文字,但是没有按下enter键,它是不能感知的,而且可以修改用户以前键入的部分;但是如果用户按下enter键,这部分输入就被感知并不可修改了,产生了新近输入的文字,并且激发一系列的反应。
3.当然,它本身是一个JComponent,我们可以从中获得。
根据以上的能力,我们就得到了具有以下功能的接口:
这里涉及到一种设计模式即称为监听器模式,或者叫观察者模式。可以参考相关资料进一步理解。这是对应于功能2的具体设计。有些人称之为事件驱动模型,意思是一个组件本身能够产生一系列的事件,而事件本身会激发已经定义的行为。这种行为是可控的,自我定义的。例如海水涨潮了是一个事件,我们可以添加一个行为,例如加高堤坝。也可以定义另一个行为,例如开闸放水。
好了,有了这个工具,就已经无压力的实现我们的功能了。
这样就够了,你已经完成了一个控制台的实现细节。现在所差的就是一个图形界面,但是对于这个方面,你也已经在接口定义和实现约束上有了控制。下面要么你可以自己去学习相关的SWING知识,或者请一个别的熟悉这方面知识的人来完成剩下的工作。这里我给一个简单的实现:
至此, 将代码copy并编译,可以发现能够使用的。下面给出一个简单的测试。
如此一来,一切就完成了。而我想要阐述的道理不在于这个实现,而是在于无论个人的能力如何,都能够融入团队,做自己力所能及的事情。所以如果面临一个似乎超出自己能力的任务时,先做自己能够做的,并把自己不能做的得到有效的封装。
好了闲话不说,首先阐述我写这篇博客的目的吧。我想采用JAVA实现一个简单的控制台来浅谈关于软件工程方面的相关技术和概念。也许这些都是次要的,我的终极目标是想通过一个简单的不能再简单的实例,来一步步地体验软件开发的全过程,并掌握软件开发里的基本交流语言。并且,在此基础上,让那些对自己技术实力没有过多自信的人找到合适的切入点,让他们自己能够真正领悟到:软件开发是个极度包容的过程,几乎任何人都可以参与。他们所要学习的,就是在这个过程中,如何扬长避短,并把自己的技术短板限制在可控的范围内,以求自身真正地融入团队中。
任何软件开发事业,首先需要明确需求。我不想过多的谈论需求的重要性,它可能会占用过多的篇幅。我只想说,在需求明确之前,永远不要进行下一步。你要在动笔写代码之前就要搞清楚你所要实现的东西是什么样子,而不是在开发过程中逐渐熟悉它是什么样子。
下面述说我的需求:
即实现一个控制台。所谓控制台,是指类似于windows系统的cmd的东西。显然它的功能包括:1.能够输出字符串到屏幕上;2.能够接受用户输入从而获取字符串。
在这里我想通过JAVA实现。用什么语言无所谓,关键是阐述的道理都是一致的。java是我最熟悉的语言,我用它已经三年了。
然后等一下,我在这里想要多加一个需求。我想要阐述一下有关组件的概念。我希望我所实现的控制台,它是一个组件,而不仅仅是一个应用程序。所谓组件,就是能够提供一个或一系列相关功能的工具包。在java里,它可能是一个类,也可能是几个类联合起来实现一个功能。总之,它是可以被其他更高层的程序利用的。所以,我在这里添加一个需求,也许与最终的用户无关,但是对于开发人员却是必要的:即作为一个组件,为其他应用程序提供可访问的接口。事实上,软件开发就是某种程度的组件组合。
好吧,总结一下三个需求:
1.能够输出字符串到屏幕上;
2.能够接受用户输入从而获取字符串。
3.作为一个组件,为其他应用程序提供可访问的接口。
明确需求过后,需要进一步细化需求,或者可以称之为大家熟悉的案例。那我就随意细化之了。为了节省篇幅,我就并不写的很详细了。平常大家开发时,可以口头交流细节,但最好还是要文档化。在这里,我的控制台跟大家见过的差不多。就是可以输出字符串,光标总是在最后面,接受字符串一直到按下enter键才有反应等等。事实上这些不重要,我的需求的三个功能已经很明确了,它们只不过是不够详细罢了。详细和明确并不对等。
接下来可以真正考虑去实现了。首先我定义一个接口,称之ConsoleInterface。其具体代码如下:
import javax.swing.JComponent; public interface ConsoleInterface { void write(char[] content); //将字符数组的内容写到Console char[] read(int num); //从Console中读取num个字符 JComponent getComponent(); //返回Console的图形化表示 }这里采用的是接口和实现分离的模式。其实整个设计模式的方法学都是在围绕这个模式进行的。这在当你感到实现时无所适从时尤其有用,也在你预料到你的实现是有局限性,以后可能会被另外的实现替代也是有用的。总之,是对于自己不会做以及预料到以后的维护时是行之必要的。所以,当自己感到自己似乎在完成一个超出自己能力的事情时,不要过分沮丧,先定义一个接口放在那里,日后再来处理也不迟。这就是第一个准则:当自己不能完成任务时,尝试定义一个接口,这样它就总在那里,而不是一个空白。
另外,需要说明的是,这个准则不要过分滥用。亦即,不要遇到什么就尝试去定义一个接口。如果你的功能你能够一气呵成地完成,而且根本不用担心日后的维护时,应该放弃先定义接口的方式。毕竟,接口与实现分离是一种曲线救国的方式,是为了长久性的考虑或者是不得已而为之的结果。这里,我并不打算让自己采用接口的方式,毕竟只是个小项目,而且我对自己完成它是充满信心的。好了,故而我生命一个类,它有个更简洁优雅的名称,如下:
//Console版本1 import javax.swing.JComponent; public class Console { //将字符数组的内容写到Console public void write(char[] content) { } //从Console中读取num个字符 public char[] read(int num) { return null; } //返回Console的图形化表示 public JComponent getComponent() { return null; } }它就是这个样子,没有过多的细节了。方法实现就是一个空白或者return null(如果你愿意,return null也可以不要)。当然,这可以替换成诸如“throw new UnsupportedException()"之类。这就是第二个准则:不要一开始就杀入细节(当然,你很天才就例外),从骨架开始,慢慢理清思路。
这个时候不要一筹莫展,先实现一部分再说。首先,不去考虑有关图形界面的部分,它可能是一个JTextArea,也可能是一个JPanel或者别的是什么,但先不去考虑这么多了,我们只需假设它具有下面的能力即可:
1.如果给它一个char数组,它能够立刻显示其中的内容。
2.如果用户输入一段文字,但是没有按下enter键,它是不能感知的,而且可以修改用户以前键入的部分;但是如果用户按下enter键,这部分输入就被感知并不可修改了,产生了新近输入的文字,并且激发一系列的反应。
3.当然,它本身是一个JComponent,我们可以从中获得。
根据以上的能力,我们就得到了具有以下功能的接口:
import javax.swing.JComponent; public interface ConsoleComponent { public void write(char[] content); public void setInputListener(InputListener l); public JComponent getComponent(); }
public interface InputListener { public void processInputData(char[] inputData); }
这里涉及到一种设计模式即称为监听器模式,或者叫观察者模式。可以参考相关资料进一步理解。这是对应于功能2的具体设计。有些人称之为事件驱动模型,意思是一个组件本身能够产生一系列的事件,而事件本身会激发已经定义的行为。这种行为是可控的,自我定义的。例如海水涨潮了是一个事件,我们可以添加一个行为,例如加高堤坝。也可以定义另一个行为,例如开闸放水。
好了,有了这个工具,就已经无压力的实现我们的功能了。
import java.util.concurrent.BlockingQueue; import javax.swing.JComponent; public class Console { private BlockingQueue<Character> charBuffer = new LinkedBlockingQueue<>(); private ConsoleComponent comp; public Console(ConsoleComponent comp) { this.comp = comp; comp.setInputListener(new InputListener() { @Override public void processInputData(char[] inputData) { try { for(char c: inputData) charBuffer.put(c); charBuffer.put('\n'); } catch(InterruptedException ex) { throw new RuntimeException(ex); } } }); } //将字符数组的内容写到Console public void write(char[] content) { comp.write(content); } //从Console中读取num个字符 public char[] read(int num) { try { char[] content = new char[num]; for(int i = 0; i < num; i++) { content[i] = charBuffer.take(); } return content; } catch(InterruptedException ex) { throw new RuntimeException(ex); } } //返回Console的图形化表示 public JComponent getComponent() { return comp.getComponent(); } }
这样就够了,你已经完成了一个控制台的实现细节。现在所差的就是一个图形界面,但是对于这个方面,你也已经在接口定义和实现约束上有了控制。下面要么你可以自己去学习相关的SWING知识,或者请一个别的熟悉这方面知识的人来完成剩下的工作。这里我给一个简单的实现:
import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; import javax.swing.JTextArea; import javax.swing.text.BadLocationException; public class TextAreaConsoleComponent implements ConsoleComponent { private JTextArea textArea = new JTextArea(); private int nextStart = textArea.getDocument().getLength(); private InputListener listener; private List<Character> buffer = new ArrayList<>(); public TextAreaConsoleComponent() { textArea.addKeyListener(new MyKeyListener()); textArea.setEditable(false); } @Override public void write(char[] content) { textArea.append(new String(content)); nextStart = textArea.getDocument().getLength(); } @Override public void setInputListener(InputListener l) { listener = l; } @Override public JComponent getComponent() { return textArea; } private class MyKeyListener extends KeyAdapter { @Override public void keyReleased(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { if(!buffer.isEmpty()) { try { textArea.getDocument().remove( textArea.getDocument().getLength() - 1, 1); } catch (BadLocationException ex) { throw new RuntimeException(ex); } buffer.remove(buffer.size() - 1); } } else if(e.getKeyCode() == KeyEvent.VK_ENTER) { char[] chars = new char[buffer.size()]; for(int i = 0; i < chars.length; i++) { chars[i] = buffer.get(i); } buffer.clear(); textArea.append("\n"); listener.processInputData(chars); } else { buffer.add(e.getKeyChar()); textArea.append(String.valueOf(e.getKeyChar())); } } } }
至此, 将代码copy并编译,可以发现能够使用的。下面给出一个简单的测试。
import javax.swing.JFrame; public class Test { public static void main(String[] args) throws Exception { Console console = new Console(new TextAreaConsoleComponent()); JFrame frame = new JFrame(); frame.add(console.getComponent()); frame.setSize(500, 400); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); console.write("Hello World".toCharArray()); char[] str = console.read(10); System.out.println(str); } }
如此一来,一切就完成了。而我想要阐述的道理不在于这个实现,而是在于无论个人的能力如何,都能够融入团队,做自己力所能及的事情。所以如果面临一个似乎超出自己能力的任务时,先做自己能够做的,并把自己不能做的得到有效的封装。
相关文章推荐
- 通过socket和Udp协议简单实现一个群体聊天工具(控制台)
- 8_14 日学到的新知识(简单的工厂模式的实现, MVC 模式的基本概念,软件工程中的四种开发模型, 以及软件工程中的一些小知识点)
- 通过socket和Udp协议简单实现一个群体聊天工具(控制台)
- 重温WCF之构建一个简单的WCF(一)(1)通过控制台和IIS寄宿服务
- SVM实现多分类的程序基础工作(二)——通过一个简单libsvm例子迈入libsvm学习的大门
- 通过perl实现一个简单的NIDS
- TensorFlow入门,基本介绍,基本概念,计算图,pip安装,helloworld示例,实现简单的神经网络
- Apple's OpenCL——以一个简单的实例介绍各个基本概念(转)
- 通过js简单实现将一个文本内容转译成加密文本
- 实现简单的MVC模式,通过一个小例子,不是很完美,但是可以说明一部分的问题
- 使用HBase协处理器---基本概念和regionObserver的简单实现
- 通过一个简单的数据库操作类了解PHP链式操作的实现
- go实现一个简单的游戏服务器框架(lotou)基本设计
- 重温WCF之构建一个简单的WCF(一)(2)通过Windows Service寄宿服务和WCF中实现操作重载
- 使用EF6和MVC5实现一个简单的选课系统--使用EF6实现基本的GRUD功能(2/12)
- HBase 协处理器---基本概念和regionObserver的简单实现
- 简单完整的代码,通过这个代码你将对RSA加密算法在Java中的实现方法有一个初步的了解,这个类,你可以直接使用,水平高的,就自己修改完善下代码。
- iOS开发UI篇—实现一个简单的手势解锁应用(基本) - 文顶顶
- 在ASP.NET Core中通过EF Core实现一个简单的全局过滤查询
- 通过编写一个简单的漏洞扫描程序学习Python基本语句