關於某道C#上機題的OO

來源:互聯網
上載者:User

前兩天在園子裡,有人出了一道《關於一道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這一輪需要去除的玩家,這樣保證了報數的連續性,實在大為不爽,不知道大家有什麼好的方法來解決呢?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.