用MVC方式实现的贪吃蛇游戏,共有4个类。运行GreedSnake运行即可。主要是观察者模式的使用,我已经添加了很多注释了。
1、
/
程序名称:贪食蛇
原作者:BigF
修改者:algo
说明:我以前也用C写过这个程序,现在看到BigF用Java写的这个,发现虽然作者自称是Java的初学者,
但是明显编写程序的素养不错,程序结构写得很清晰,有些细微得地方也写得很简洁,一时兴起之
下,我认真解读了这个程序,发现数据和表现分开得很好,而我近日正在学习MVC设计模式,
因此尝试把程序得结构改了一下,用MVC模式来实现,对源程序得改动不多。
我同时也为程序增加了一些自己理解得注释,希望对大家阅读有帮助。
/
package mvcTest;
/
@author WangYu
@version 10
Description:
</pre>
Create on :Date :2005-6-13 Time:15:57:16
LastModified:
History:
/
public class GreedSnake {
public static void main(String[] args) {
SnakeModel model = new SnakeModel(20,30);
SnakeControl control = new SnakeControl(model);
SnakeView view = new SnakeView(model,control);
//添加一个观察者,让view成为model的观察者
modeladdObserver(view);
(new Thread(model))start();
}
}
-------------------------------------------------------------
2、
package mvcTest;
//SnakeControljava
import javaawteventKeyEvent;
import javaawteventKeyListener;
/
MVC中的Controler,负责接收用户的 *** 作,并把用户 *** 作通知Model
/
public class SnakeControl implements KeyListener{
SnakeModel model;
public SnakeControl(SnakeModel model){
thismodel = model;
}
public void keyPressed(KeyEvent e) {
int keyCode = egetKeyCode();
if (modelrunning){ // 运行状态下,处理的按键
switch (keyCode) {
case KeyEventVK_UP:
modelchangeDirection(SnakeModelUP);
break;
case KeyEventVK_DOWN:
modelchangeDirection(SnakeModelDOWN);
break;
case KeyEventVK_LEFT:
modelchangeDirection(SnakeModelLEFT);
break;
case KeyEventVK_RIGHT:
modelchangeDirection(SnakeModelRIGHT);
break;
case KeyEventVK_ADD:
case KeyEventVK_PAGE_UP:
modelspeedUp();
break;
case KeyEventVK_SUBTRACT:
case KeyEventVK_PAGE_DOWN:
modelspeedDown();
break;
case KeyEventVK_SPACE:
case KeyEventVK_P:
modelchangePauseState();
break;
default:
}
}
// 任何情况下处理的按键,按键导致重新启动游戏
if (keyCode == KeyEventVK_R ||
keyCode == KeyEventVK_S ||
keyCode == KeyEventVK_ENTER) {
modelreset();
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
-------------------------------------------------------------
3、
/
/
package mvcTest;
/
游戏的Model类,负责所有游戏相关数据及运行
@author WangYu
@version 10
Description:
</pre>
Create on :Date :2005-6-13 Time:15:58:33
LastModified:
History:
/
//SnakeModeljava
import javaxswing;
import javautilArrays;
import javautilLinkedList;
import javautilObservable;
import javautilRandom;
/
游戏的Model类,负责所有游戏相关数据及运行
/
class SnakeModel extends Observable implements Runnable {
boolean[][] matrix; // 指示位置上有没蛇体或食物
LinkedList nodeArray = new LinkedList(); // 蛇体
Node food;
int maxX;
int maxY;
int direction = 2; // 蛇运行的方向
boolean running = false; // 运行状态
int timeInterval = 200; // 时间间隔,毫秒
double speedChangeRate = 075; // 每次得速度变化率
boolean paused = false; // 暂停标志
int score = 0; // 得分
int countMove = 0; // 吃到食物前移动的次数
// UP and DOWN should be even
// RIGHT and LEFT should be odd
public static final int UP = 2;
public static final int DOWN = 4;
public static final int LEFT = 1;
public static final int RIGHT = 3;
public SnakeModel( int maxX, int maxY) {
thismaxX = maxX;
thismaxY = maxY;
reset();
}
public void reset(){
direction = SnakeModelUP; // 蛇运行的方向
timeInterval = 200; // 时间间隔,毫秒
paused = false; // 暂停标志
score = 0; // 得分
countMove = 0; // 吃到食物前移动的次数
// initial matirx, 全部清0
matrix = new boolean[maxX][];
for (int i = 0; i < maxX; ++i) {
matrix[i] = new boolean[maxY];
Arraysfill(matrix[i], false);
}
// initial the snake
// 初始化蛇体,如果横向位置超过20个,长度为10,否则为横向位置的一半
int initArrayLength = maxX > 20 10 : maxX / 2;
nodeArrayclear();
for (int i = 0; i < initArrayLength; ++i) {
int x = maxX / 2 + i;//maxX被初始化为20
int y = maxY / 2; //maxY被初始化为30
//nodeArray[x,y]: [10,15]-[11,15]-[12,15]~~[20,15]
//默认的运行方向向上,所以游戏一开始nodeArray就变为:
// [10,14]-[10,15]-[11,15]-[12,15]~~[19,15]
nodeArrayaddLast(new Node(x, y));
matrix[x][y] = true;
}
// 创建食物
food = createFood();
matrix[foodx][foody] = true;
}
public void changeDirection(int newDirection) {
// 改变的方向不能与原来方向同向或反向
if (direction % 2 != newDirection % 2) {
direction = newDirection;
}
}
/
运行一次
@return
/
public boolean moveOn() {
Node n = (Node) nodeArraygetFirst();
int x = nx;
int y = ny;
// 根据方向增减坐标值
switch (direction) {
case UP:
y--;
break;
case DOWN:
y++;
break;
case LEFT:
x--;
break;
case RIGHT:
x++;
break;
}
// 如果新坐标落在有效范围内,则进行处理
if ((0 <= x && x < maxX) && (0 <= y && y < maxY)) {
if (matrix[x][y]) { // 如果新坐标的点上有东西(蛇体或者食物)
if (x == foodx && y == foody) { // 吃到食物,成功
nodeArrayaddFirst(food); // 从蛇头赠长
// 分数规则,与移动改变方向的次数和速度两个元素有关
int scoreGet = (10000 - 200 countMove) / timeInterval;
score += scoreGet > 0 scoreGet : 10;
countMove = 0;
food = createFood(); // 创建新的食物
matrix[foodx][foody] = true; // 设置食物所在位置
return true;
} else // 吃到蛇体自身,失败
return false;
} else { // 如果新坐标的点上没有东西(蛇体),移动蛇体
nodeArrayaddFirst(new Node(x, y));
matrix[x][y] = true;
n = (Node) nodeArrayremoveLast();
matrix[nx][ny] = false;
countMove++;
return true;
}
}
return false; // 触到边线,失败
}
public void run() {
running = true;
while (running) {
try {
Threadsleep(timeInterval);
} catch (Exception e) {
break;
}
if (!paused) {
if (moveOn()) {
setChanged(); // Model通知View数据已经更新
notifyObservers();
} else {
JOptionPaneshowMessageDialog(null,
"you failed",
"Game Over",
JOptionPaneINFORMATION_MESSAGE);
break;
}
}
}
running = false;
}
private Node createFood() {
int x = 0;
int y = 0;
// 随机获取一个有效区域内的与蛇体和食物不重叠的位置
do {
Random r = new Random();
x = rnextInt(maxX);
y = rnextInt(maxY);
} while (matrix[x][y]);
return new Node(x, y);
}
public void speedUp() {
timeInterval = speedChangeRate;
}
public void speedDown() {
timeInterval /= speedChangeRate;
}
public void changePauseState() {
paused = !paused;
}
public String toString() {
String result = "";
for (int i = 0; i < nodeArraysize(); ++i) {
Node n = (Node) nodeArrayget(i);
result += "[" + nx + "," + ny + "]";
}
return result;
}
}
class Node {
int x;
int y;
Node(int x, int y) {
thisx = x;
thisy = y;
}
}
------------------------------------------------------------
4、
package mvcTest;
//SnakeViewjava
import javaxswing;
import javaawt;
import javautilIterator;
import javautilLinkedList;
import javautilObservable;
import javautilObserver;
/
MVC模式中得Viewer,只负责对数据的显示,而不用理会游戏的控制逻辑
/
public class SnakeView implements Observer {
SnakeControl control = null;
SnakeModel model = null;
JFrame mainFrame;
Canvas paintCanvas;
JLabel labelScore;
public static final int canvasWidth = 200;
public static final int canvasHeight = 300;
public static final int nodeWidth = 10;
public static final int nodeHeight = 10;
public SnakeView(SnakeModel model, SnakeControl control) {
thismodel = model;
thiscontrol = control;
mainFrame = new JFrame("GreedSnake");
Container cp = mainFramegetContentPane();
// 创建顶部的分数显示
labelScore = new JLabel("Score:");
cpadd(labelScore, BorderLayoutNORTH);
// 创建中间的游戏显示区域
paintCanvas = new Canvas();
paintCanvassetSize(canvasWidth + 1, canvasHeight + 1);
paintCanvasaddKeyListener(control);
cpadd(paintCanvas, BorderLayoutCENTER);
// 创建底下的帮助栏
JPanel panelButtom = new JPanel();
panelButtomsetLayout(new BorderLayout());
JLabel labelHelp;
labelHelp = new JLabel("PageUp, PageDown for speed;", JLabelCENTER);
panelButtomadd(labelHelp, BorderLayoutNORTH);
labelHelp = new JLabel("ENTER or R or S for start;", JLabelCENTER);
panelButtomadd(labelHelp, BorderLayoutCENTER);
labelHelp = new JLabel("SPACE or P for pause", JLabelCENTER);
panelButtomadd(labelHelp, BorderLayoutSOUTH);
cpadd(panelButtom, BorderLayoutSOUTH);
mainFrameaddKeyListener(control);
mainFramepack();
mainFramesetResizable(false);
mainFramesetDefaultCloseOperation(JFrameEXIT_ON_CLOSE);
mainFramesetVisible(true);
}
void repaint() {
Graphics g = paintCanvasgetGraphics();
//draw background
gsetColor(ColorWHITE);
gfillRect(0, 0, canvasWidth, canvasHeight);
// draw the snake
gsetColor(ColorBLACK);
LinkedList na = modelnodeArray;
Iterator it = naiterator();
while (ithasNext()) {
Node n = (Node) itnext();
drawNode(g, n);
}
// draw the food
gsetColor(ColorRED);
Node n = modelfood;
drawNode(g, n);
updateScore();
}
private void drawNode(Graphics g, Node n) {
gfillRect(nx nodeWidth,
ny nodeHeight,
nodeWidth - 1,
nodeHeight - 1);
}
public void updateScore() {
String s = "Score: " + modelscore;
labelScoresetText(s);
}
public void update(Observable o, Object arg) {
repaint();
}
}
笨办法是在循环中不断读取目录以及目录中文件的最后修改时间,如果有变更,则说说文件被修改。
这种办法需要不断的循环读取文件信息,这对cpu占用较高,同时也会产生一定的磁盘IO,短时间小范围使用尚可,如果长时间运行在服务器上,或者监控一个包含很多文件的目录则不可取。
办法之二是利用 *** 作系统提供的功能来实现。
比如在Windows中,可以利用Windows APIReadDirectoryChangesW来实现。这可以免去大量的磁盘IO。因为文件修改的频率一般不高,可以使用定计时器进行循环,对cup的占用也会少很多。
package TestObserver;
import javautilIterator;
import javautilVector;
/
@author Seastar
/
interface Observed {
public void addObserver(Observer o);
public void removeObserver(Observer o);
public void update();
}
interface Observer {
public void takeAction();
}
class Invoker {
private Observer o;
Handler handler;
public Invoker(Observer o) {
new Handler();
thiso = o;
}
private class Handler extends Thread {
public Handler() {
handler = this;
}
@Override
public void run() {
otakeAction();
}
}
public boolean TestSameObserver(Observer o) {
return o == thiso;
}
public void invoke() {
handlerstart();
}
}
class ObservedObject implements Observed {
private Vector<Invoker> observerList = new Vector<Invoker>();
public void addObserver(Observer o) {
observerListadd(new Invoker(o));
}
public void removeObserver(Observer o) {
Iterator<Invoker> it = observerListiterator();
while (ithasNext()) {
Invoker i = itnext();
if (iTestSameObserver(o)) {
observerListremove(i);
break;
}
}
}
public void update() {
for (Invoker i : observerList) {
iinvoke();
}
}
}
class ObserverA implements Observer {
public void takeAction() {
Systemoutprintln("I am Observer A ,state changed ,so i have to do something");
}
}
class ObserverB implements Observer {
public void takeAction() {
Systemoutprintln("I am Observer B ,i was told to do something");
}
}
class ObserverC implements Observer {
public void takeAction() {
Systemoutprintln("I am Observer C ,I just look ,and do nothing");
}
}
public class Main {
/
@param args the command line arguments
/
public static void main(String[] args) {
ObserverA a = new ObserverA();
ObserverB b = new ObserverB();
ObserverC c = new ObserverC();
ObservedObject oo = new ObservedObject();
ooaddObserver(a);
ooaddObserver(b);
ooaddObserver(c);
for (int i = 0; i < 5; ++i) {
ooaddObserver(new Observer() {
public void takeAction() {
Systemoutprintln("我是山寨观察者"+",谁敢拦我");
}
});
}
//sometime oo changed ,so it calls update and informs all observer
ooupdate();
}
}
观察者模式的精髓在于注册一个观察者观测可能随时变化的对象,对象变化时就会自动通知观察者,
这样在被观测对象影响范围广,可能引起多个类的行为改变时很好用,因为无需修改被观测对象的代码就可以增加被观测对象影响的类,这样的设计模式使得代码易于管理和维护,并且减少了出错几率
至于异步机制实际是个噱头,可以有观测对象来实现异步,也可以有观察者自身实现,这个程序实际是观测对象实现了异步机制,方法是在观察者类外包装了一层invoker类
软件体系结构通常被称为架构,指可以预制和可重构的软件框架结构。架构尚处在发展期,对于其定义,学术界尚未形成一个统一的意见,而不同角度的视点也会造成软件体系结构的不同理解,以下是一些主流的标准观点。
ANSI/IEEE 61012-1990软件工程标准词汇对于体系结构定义是:“体系架构是以构件、构件之间的关系、构件与环境之间的关系为内容的某一系统的基本组织结构以及知道上述内容设计与演化的原理(principle)”。
Mary Shaw和David Garlan认为软件体系结构是软件设计过程中,超越计算中的算法设计和数据结构设计的一个层次。体系结构问题包括各个方面的组织和全局控制结构,通信协议、同步,数据存储,给设计元素分配特定功能,设计元素的组织,规模和性能,在各设计方案之间进行选择。Garlan & Shaw模型的基本思想是:软件体系结构={构件(component),连接件(connector),约束(constrain)}.其中构件可以是一组代码,如程序的模块;也可以是一个独立的程序,如数据库服务器。连接件可以是过程调用、管道、远程过程调用(RPC)等,用于表示构件之间的相互作用。约束一般为对象连接时的规则,或指明构件连接的形式和条件,例如,上层构件可要求下层构件的服务,反之不行;两对象不得递规地发送消息;代码复制迁移的一致性约束;什么条件下此种连接无效等。
关于架构的定义还有很多其他观点,比如Bass定义、Booch & Rumbaugh &Jacobson定义、Perry & Wolf模型[7]、Boehm模型等等,虽然各种定义关键架构的角度不同,研究对象也略有侧重,但其核心的内容都是软件系统的结构,其中以Garlan & Shaw模型为代表,强调了体系结构的基本要素是构件、连接件及其约束(或者连接语义),这些定义大部分是从构造的角度来甚至软件体系结构,而IEEE的定义不仅强调了系统的基本组成,同时强调了体系结构的环境即和外界的交互。
什么是模式
模式(Pattern)的概念最早由建筑大师Christopher Alexander于二十世纪七十年代提出,应用于建筑领域,八十年代中期由Ward Cunningham和Kent Beck将其思想引入到软件领域,Christopher Alexander将模式分为三个部分:首先是周境(Context,也可以称着上下文),指模式在何种状况下发生作用;其二是动机(System of Forces),意指问题或预期的目标;其三是解决方案(Solution),指平衡各动机或解决所阐述问题的一个构造或配置(Configuration)。他提出,模式是表示周境、动机、解决方案三个方面关系的一个规则,每个模式描述了一个在某种周境下不断重复发生的问题,以及该问题解决方案的核心所在,模式即是一个事物(thing)又是一个过程(process),不仅描述该事物本身,而且提出了通过怎样的过程来产生该事物。这一定义已被软件界广为接受。
软件模式的应用对软件开发产生了重大的作用,主要表现在:
软件模式是人们在长期的设计软件、管理组织软件开发等实践中大量经验的提炼和抽象,是复用软件设计方法、过程管理经验的有力工具。模式类似于拳击中的组合拳,它提供了一系列软件开发中的思维套路。如,通过模式的使用,有利于在复杂的系统中产生简洁、精巧的设计。
软件模式为我们提供了一套简洁通用的设计、管理、组织方面的词汇,同时模式也为我们提供了一个描述抽象事物的规范标准,可大大促进软件开发过程中人与人之间的交流,而软件开发中的交流是至关重要的,“软件项目失败的原因最终都可追溯到信息没有及时准确地传递到应该接收它的人”。
架构和模式的关系
因为架构(Architecture)和模式(Pattern)在当前的软件开发中经常地被提及,可是很多人容易混淆这两个术语,而对此,学术界也没有一个非常统一的定义。
架构和模式应该是一个属于相互涵盖的过程,但是总体来说Architecture更加关注的是所谓的High-Level Design,而模式关注的重点在于通过经验提取的“准则或指导方案”在设计中的应用,因此在不同层面考虑问题的时候就形成了不同问题域上的Pattern。模式的目标是,把共通问题中的不变部分和变化部分分离出来。不变的部分,就构成了模式,因此,模式是一个经验提取的“准则”,并且在一次一次的实践中得到验证,在不同的层次有不同的模式,小到语言实现(如Singleton)大到架构。在不同的层面上,模式提供不同层面的指导。根据处理问题的粒度不同,从高到低,模式分为3个层次:架构模式(Architectural Pattern)、设计模式(Design Pattern)、实现模式(Implementation Pattern)架构模式是模式中的层次,描述软件系统里的基本的结构组织或纲要,通常提供一组事先定义好的子系统,指定它们的责任,并给出把它们组织在一起的法则和指南。比如,用户和文件系统安全策略模型,N-层结构,组件对象服务等,我们熟知的MVC结构也属于架构模式的层次。一个架构模式常常可以分解成很多个设计模式的联合使用。设计模式是模式中的第二层次,用来处理程序设计中反复出现的问题。例如,[GOF95]总结的23个基本设计模式——Factory Pattern, Observer Pattern等等。实现模式是最低也是体的层次,处理具体到编程语言的问题。比如,类名,变量名,函数名的命名规则;异常处理的规则等等。
相对于系统分析或者设计模式来说,体系结构从更高的层面去考虑问题,所以关注的问题就体现在“不变”因素上,比如系统部署中,更加关心应用程序的分层分级设计,而在这个基础之上提出的部署方案,才是架构考虑的重点。体系结构关心应用程序模式,更加体现在通过技术去解决这些业务差异带来的影响,关心是否是分布式应用程序,关心系统分层是如何设计,也关心性能和安全,因此在这样的情况之下,会考虑集群,负载平衡,故障迁移等等一系列技术。
希望通过定义的方式来区分架构和模式是不太可能的,因为本来就是交互交叉和提供服务的,它实际上是架构模式,而不是设计模式。在大部份情况下,表现为下面几个设计模式之一:Strategy模式、Mediator模式、Composite模式、Observer模式。对于熟悉架构设计的系统架构师而言,似乎可以用如下来解释架构和模式之间的关系:架构是Hight-Level Design,着眼于不同业务中共性的解决方案,而模式是General Principle(通用原理)。
以上就是关于我想求一个Java编写的贪吃蛇游戏,要有注释和流程图最好全部的内容,包括:我想求一个Java编写的贪吃蛇游戏,要有注释和流程图最好、python 监控一个文件夹、求 JAVA 异步观察者模式 的源代码(完整的),不要同步的,好的给加分等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)