关于某道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这一轮需要去除的玩家,这样保证了报数的连续性,实在大为不爽,不知道大家有什么好的方法来解决呢?

时间: 2024-09-27 06:51:26

关于某道C#上机题的OO的相关文章

C#上机题的OO

前段时间看到某人<关于某道C#上机题的OO> ,后来又有人用了装饰模式做这题,我这里来个策略模式,不习惯废话直接上代码,不知道算不算策略模式,请高人指点. 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 namespace ConsoleApp 5 { 6 public class Program 7 { 8 static void Main(string[] args) 9 { 10 Gam

有道难题第一题非OO解,极端记录160ms

测试平台: P8600 4G 目前看见最高效率的是夜咖啡的,我这里的数据是稳定在195-200ms上下. 然后是eaglet,基本是400ms 我这个代码稳定在170ms左右 我的这个代码主要思路 1.在原有数组外围增加一圈0,这样就降低了统计时候的复杂度 2.将一维字符串数组转换为一个字符串,其实这也是增加0的副产品,如果有朋友能维持一维字符串 数组并增加0请告知一下 3.在最后的一维数组中直接用坐标计算方式算出当前位置的相关8个下标并直接计算 4.累加后统一减384,而不是每次减'0'字符

C#上机题

本文继续<C#上机题的OO - 策略模式>中的题目,但这是使用的是双向循环链表.当第一次看到这题我首先想到的是循环链表,但题目要求面向对象的方法,汗~ 首先是双向链表的节点类 1 /// <summary> 2 /// 双向链表节点 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class DoubleLinkNode<T> 6 {

一个道c++的题(用c++做,要详细代码)

问题描述 一个道c++的题(用c++做,要详细代码) 有三个海军陆战队wilyin的基地.他们的位置形成一个直角三角形.现在wilyin得到另一个海洋他想把它放在某个地方形成一个矩形与前三名海军陆战队员.他应该把它放在哪里?输入输入的第一行包含一个整数T这意味着测试用例的数量.然后T行每一行包含6个正整数x1y1x2y2 x3y3这意味着这三个海军陆战队员的位置.你可能认为不协调的绝对值超过3000人.输出对于每个案例打印出来的协调海洋在一行.输入20 0 1 0 0 10 1 0 -1 1 0

刷华为上机题库时遇到的一道编程题

问题描述 刷华为上机题库时遇到的一道编程题 题目的要求是输入一个字符串,对字符串做如下变化1:按照A-Z的顺序条换顺序,不管字母的大小写.如Type变换为epTy2:同一个英文字母的大小写同时存在时,按照输入顺序排列.如输入:BabA?输出:aABb3:非英文字母的其它字符保持原来的位置.输入:By?e?输出:Be?y #include ""iostream""#include ""string""using namespac

自适应滤波-谁会这道离散随机信号处理的上机题啊?

问题描述 谁会这道离散随机信号处理的上机题啊? 就是要用MATLAB编程,希望附上完整程序,不是那种调不出来的-毕竟不是程序猿,也不太用MATLAB,只能献上我的膝盖了,大神! 解决方案 题目如图,希望是完整的代码,也可以发邮箱992239684@qq.com,不胜感激!

c c++ acm-浙江大学PAT上机题,求解析. 表达式转换

问题描述 浙江大学PAT上机题,求解析. 表达式转换 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间.请设计程序将中缀表达式转换为后缀表达式. 输入格式说明: 输入在一行中给出不含空格的中缀表达式,可包含+.-.*.以及左右括号(),表达式不超过20个字符. 输出格式说明: 在一行中输出转换后的后缀表达式,要求不同对象(运算数.运算符号)之间以空格分隔,但结尾不得有多余空格. 样例输入与输出: 序号 输入 输出 1 2+

汤姆大叔的6道javascript编程题题解

看汤姆大叔的博文,其中有篇(猛戳这里)的最后有6道编程题,于是我也试试,大家都可以先试试. 1.找出数字数组中最大的元素(使用Math.max函数) var a = [1, 2, 3, 6, 5, 4];  var ans = Math.max.apply(null, a);  console.log(ans);  // 6  这题很巧妙地用了apply,如果不是数组,是很多数字求最大值,我们知道可以这样: var ans = Math.max(1, 2, 3, 4, 5, 6);  conso

有道难题第二题最新算法(不仅仅是速度)

最近好像算法问题又不热门了,米关系,自己喜欢就好.我的有道第二题不是双倍超立方,也不知道是什么算法,大概的题目意思,大家可以参考:"我的有道第二题(不是双倍超立方)"   其实一开始我觉得很简单,我写的第一个解法也是正确的,不过讨厌在题目说,输入的n可以为2,000,000,000. 20亿.....如果N为最大的时候,不用看了,我的解法直接out,第一时间很长,第二,直接out of Memory   那该怎么做呢???我寻思着它的规律,其实一直有种感觉,可一直把握不住,而且平时也没