关于某道C#上机题的OO
前两天在园子里,有人出了一道《关于一道C#上机题的一点想法》,大概的意思呢是利用OO的思想来进行编程,接着又有一位朋友,也写了自己的答案,此朋友非常厉害,从类图,接口,封装,多态,都一一实现,实在让我佩服,不过真有点过度设计的味道,接着又有一大虾,完成了自己的OO答案,把泛型,可变,不可变都一一列举,实在令人佩服啊,可我觉得,或许是我理解错了,但我觉得三位,你们都偏离了题目,偏离了OO,你们只是利用了OO的特性。
题目
17个人围成一圈,从第一个人开始报数,报到3的退出,一直到剩下最后一个人,用面向对象的思想去做这道题。
点评
我不是高手,没什么资格点评大家,只是提出自己的看法。
Joyaspx 只实现了一个对象,那就是人,但是却把“到3退出”给放在执行方法中,而人这个对象,还要知道他的哥哥弟弟,或许是Joyaspx上机时间不够,感觉这个方式不是面向对象的进行开发,还是用了面向问题来解决了。
OOLi 不得不佩服,OO的一切,从设计到接口到实现都一一实现,实在是过度设计了,但其中的OO实在不敢恭维,比如初始数据时,使用了硬编码,第一个人还需要给他一个编号,还给Person这个对象配备了一个State,根据State来判断是否该移除,他的退出也很有趣,把自己割掉。。。。告诉哥哥,你没有我这个弟弟,你的弟弟是我的小弟弟,那我想问下,我去哪里了?
YangQ 这位仁兄,我不得不说下,你的程序真的不是面向对象,是完全的面向过程来开发,虽然你用到了泛型,但不是说用了泛型就是面向对象开发了,希望兄台能继续努力,掌握和了解一下什么是面向对象开发。
我的理解
题目很短,我们也应该很好理解他,一共只有一个对象,那就是人Person,这是没有错误的,大家都想到的。但在这到题目中,并没有说我需要知道下一个人是谁,上一个人是谁,因为他们都是在玩游戏,一个报数的游戏,“到3退出”只是游戏的一个规则,不是每一个人都需要玩这个游戏,我们只需要17个人而已,所以对Person对象而言,并不需要那么复杂的Perv,Next,包括退出的动作,也不属于“人”的范畴,只是“人”在“报数游戏”的场景中,对于OO编程来说,一切皆对象,也就是说,游戏也是对象,呵呵。
此题是非常微妙的,如果没有要求OO的话,它应该是一个数据结构的算法问题,也就是前几位大哥说的那种,是什么结构我叫不出来,我自己认为是一个环状的,大家手拉手拉成圈的。
开始
理解了题目,我们知道需要2个对象,Person,Game,游戏必须依赖于人,因为没有人,游戏也不会开始,人不需要知道游戏,只要参加的人了解游戏就可以。我们看下Person对象的定义:
public class Person
{
public Person(int personID)
{
this.PersonID = personID;
}
public int PersonID { get; set; }
public void Say()
{
if (this.Said != null) this.Said(this, new PersonEventArgs(this));
}
public event EventHandler<PersonEventArgs> Said;
}
每一个人都有自己的ID,因为是演示,姓名之类的,我就不加入了。有一个Say的方法,因为我们报数需要嘴巴来说,其中呢也不执行什么内容,如果需要内容,我们可以自己添加。对于人来说,我们每次说话不一定需要每次自己或者别人来做出响应,但我需要通知某一个对象,我说话了,就算你是对墙说话,你还是通知了墙,“Hi,墙,我说话了”,所以我加入了Said一个委托事件,目的是把我说话了通知给某个对象,在这个题目中,我通知给“游戏”这个对象,这应该属于通知模式了吧,呵呵。
PersonEventArgs:
public class PersonEventArgs : EventArgs
{
public PersonEventArgs(Person person)
{
this.Person = person;
}
public Person Person { get; set; }
}
接下来重点说说游戏,对于我们其他人来说(除了游戏中人),我是裁判,我只需要说游戏开始,就可以了,达到某个条件的时候,Game Over。所以我们只需要发命令,让游戏开始就好了。
Game game = new Game(17); //17 代表参加的人数
game.Start();
这是程序测试的接口了,那我们构造这个Game对象就相对简单了,因为只要告诉它,多少人参加,然后游戏开始就OK了,我们只需要公开一个构造函数,一个开始方法就好了。
public class Game
{
public Game(int personNumber)
{
}
public void Start()
{
}
}
这样我们完成了封装,呵呵,对于外部,我们只需要知道这些已经足够了,那接下来,我们看看Game中,我们还需要些什么。
既然我们需要人,而且是很多人玩游戏,那一定有一个Players的属性,游戏开始呢,需要开始报数,这时候我们需要一个一个人去进行报数,报数的结果呢,是游戏的一个状态(注意,是对象的状态,不是类型的),我们看下我写的Game类:
public class Game
{
private int CurrentNumber = 0;
private List<Person> CurrentQuitPersons = new List<Person>();
private List<Person> Players { get; set; }
private event EventHandler<PersonEventArgs> GameOver;
public Game(int personNumber)
{
Ready(personNumber);
}
public void Start()
{
++CurrentNumber;
this.GameOver += new EventHandler<PersonEventArgs>(Game_GameOver);
Go();
}
private void Ready(int personNumber)
{
this.Players = new List<Person>(personNumber);
for (int i = 0; i < personNumber; i++)
{
Person person = new Person(i);
person.Said += new EventHandler<PersonEventArgs>(Person_Said);
this.Players.Add(person);
}
}
private void Go()
{
var persons = this.Players;
persons.ForEach(p =>
{
p.Say();
CurrentNumber++;
});
if (this.Players.Count > 1)
{
if (CurrentQuitPersons.Any())
{
this.Players.RemoveAll(p => CurrentQuitPersons.Contains(p));
CurrentQuitPersons.Clear();
}
Go();
}
else
{
this.GameOver(this, new PersonEventArgs(this.Players.First()));
}
}
private void Person_Said(object sender, PersonEventArgs e)
{
if (CurrentNumber % 3 == 0)
{
CurrentQuitPersons.Add(e.Person);
Console.WriteLine("The player quit, ID : {0}, CurrentNumber:{1}", e.Person.PersonID, CurrentNumber);
}
}
private void Game_GameOver(object sender, PersonEventArgs e)
{
Console.WriteLine("Last Person's Person ID is {0}", e.Person.PersonID);
Console.WriteLine("Game Over.");
}
}
呵呵,不好意思,比较长,请大家耐心看完。
其中呢有一个CurrentNumber字段,代表着这个Game对象的一个当前状态,也就是报数的一个数字。Players呢,是参加的人员,在构造函数的时候,会去准备一下,也就是初始化这个Players属性,每一个人呢,我们会分配一个ID,然后会委托一个Person_Said的委托,目的是让Game知道,Play报数了,然后根据这个数多少来反应一个动作。这个题目中呢,也就是“到3退出”。
一切都准备好了之后,我们就开始Start了,刚开始,从1开始,当前数字转变为1(为了区分结果,我把人的初始序号,是从0开始的),每个人开始报数,在Go这个方法中呢,会判断一下,如果还剩下一个人的时候,游戏结束,好,我们看下运行结果吧。
ok,程序结束,运行正确,也是我们预料的。
总结
这次呢,正好有时间,有机会让自己体验一下面向对象的编程,其实题目并不是很难,要看大家的理解是如何的,不是说用了面向对象的特性就是面向对象的一个开发,这完全是一个误区,就好象你在项目中,用了一个接一个的模式一样,模式狂人并不代表你的程序是一个模式的程序,模式是在开发以后逐渐形成,能让我们更好的进行扩展、封装等,让每个人能更好的理解(比如UML),所以面向对象也是一样,它的特性完全是因为在开发过程中,人们发觉了这些特性,把它列举出来,并形成了一个规范文档,让大家能快速的上手了解面向对象,并不是说有了这些特性,就是面向对象开发。再通俗一点,歌手的特性会唱歌,但不是会唱歌的人就是歌手一样。
不足
我不能说我的解答非常完美,只是借此机会阐述自己的一些看法和观点。不足之处也有,因为我完全没有考虑算法,完全没有考虑性能。除此之外,其中也有一个败笔,那就是CurrentQuitPersons这个字段,原先我想是在Person_Said的时候,到3直接退出Players的,但发觉Remove后,序号会直接重新排列,造成了误差,所以利用这个字段,我在每一轮结束的时候,Remove这一轮需要去除的玩家,这样保证了报数的连续性,实在大为不爽,不知道大家有什么好的方法来解决呢?
- 获取可视区域高度赋值给div(解决document.body.clientHeight的返回值为0的问题)
- Docker管理工具-Swarm部署记录
- 聊一聊大数据的问题和缺陷
- Flash/Flex学习笔记(13):对象拖动(startDrag/stopDrag)
- 原来Silverlight 4中是可以玩UDP的!
- Flash/Flex学习笔记(12):FMS 3.5之如何做视频实时直播
- Flash/Flex学习笔记(11):如何检测摄像头是否被占用
- Flash/Flex学习笔记(10):FMS 3.5之Hello World!
- Swarm基于多主机容器网络-overlay networks 梳理
- 未来AI可能会淘汰180万个工作岗位,你感到恐惧了吗
- css基础:把所有背景图都集成在一张图片上,减少图片服务器请求次数
- Docker可视化界面(Consul+Shipyard+Swarm+Service Discover)部署记录
- windows 2008上启用防火墙后sqlserver 2005经常出现连接超时的解决办法
- 重温delphi之控制台程序:Hello World!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 当一个http请求来临时,SpringMVC究竟偷偷帮你做了什么?SpringMVC视图处理器与视图篇章【终章】
- 求求你,别再开发的时候一用redis分布式锁,就急着去复制粘贴了!lua脚本的实现思路
- 全网最详细的 K8s Service 不能访问排查流程
- 团体程序设计天梯赛-练习集 L1-001 Hello World
- 团体程序设计天梯赛-练习集 L1-002 打印沙漏
- 团体程序设计天梯赛-练习集 L1-003 个位数统计
- 团体程序设计天梯赛-练习集 L1-004 计算摄氏温度
- 团体程序设计天梯赛-练习集 L1-007 念数字
- 团体程序设计天梯赛-练习集 L1-008 求整数段和
- K8s多租户场景下的多层级namespace规则解析
- 团体程序设计天梯赛-练习集 L1-010 比较大小
- 团体程序设计天梯赛-练习集 L1-012 计算指数
- 团体程序设计天梯赛-练习集 L1-013 计算阶乘和
- 团体程序设计天梯赛-练习集 L1-036 A乘以B
- 团体程序设计天梯赛-练习集 L1-015 跟奥巴马一起画方块