質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Q&A

7回答

7493閲覧

オブジェクト指向でクラスを作成する際の考え方について

mm--_--mm

総合スコア113

Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

0グッド

9クリップ

投稿2016/05/06 00:50

いつもお世話になっております。
オブジェクト指向プログラミングについて勉強中の者です。

表題の件ですが、オブジェクト指向プログラミングをするにあたってクラスを作成する時があると思いますが、皆様はクラスを作成する際に、どのような考え方でクラスを作成してますでしょうか?

例えば、じゃんけんゲームを作成するとして私の場合、自分クラス、相手クラス、結果クラスを作成しその中でグー、チョキ、パー等の変数を用意して結果クラスに値を渡して結果を返して表示するというような考え方になります。

じゃんけんゲームというような単純なシステムならともかく、これが大きなシステムを作るとなった時に、どのような考え方でクラスを作成すれば誰が読んでも理解しやすいコードになるか教えていただけると幸いです。

そんなのシステムの仕様によっていくらでも変わってくるという風に思えてきますが、
皆様のクラスを作成する場合はこのように考えてクラスを作成しているという様な事を教えていただければと思います。

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答7

0

クラスを作成する場合はこのように考えてクラスを作成しているという様な事を教えていただければ

クラスに限らずですが「そもそも論」なんです。そもそもなぜクラスを作るんでしょう?究極的に言えばそのほうが楽だからです。この楽には将来の楽、他人の楽も含まれます。
問題は未来に何が起こるか分からないことで、これに対しては大きく2つの態度があります。一方には拡張性を高めた、「備えあれば憂いなし」的な態度。オブジェクト指向の教科書には性質上・目的上この方向のものが多いですね(カプセル化、継承による拡張性、…)。もう一方にはYAGNI原則といって、不確定な未来の事を考慮せず、現在確実な要件に集中する態度です。

「クラスを作成する場合」は、その方が楽になると考えている場合で、何をもって楽たりえると思うかは態度や考え方、経験によって変わります。答えは無いので毎回自問するしかありません。この部分が思考停止するとなんでもかんでもクラスにしたり、未来永劫1クラスにしか継承されない基底クラスを作ったり・・するようになります。勉強の目的は手段の追加であって上書きではありません。思考停止さえしなければ良い解には近づいていけると思います。

じゃんけんゲームに戻ると、単純なコンソールで動くじゃんけんなら、私はクラスにしません。めんどくさがり屋なのでエントリーポイントの関数(main)だけで終わらせちゃうかもしれません。でももしMassively MultiplayerでOnlineなじゃんけん(統計的勝負!?)だったら、オンライン中のプレイヤーの数だけインスタンスがあるという設計は最初に検討すると思います。逆にそうしないととってもめんどくさいことになる予感がするんです。

そんな感じで私の場合は「めんどくさい駆動開発」です。「クラスにしないと後々めんどくさそう」と思ったらクラスにします(その閾値は言語によって異なります)。

投稿2016/05/06 04:09

sharow

総合スコア1149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

私も勉強中の者ですが、私なりに学んだ考え方でジャンケンを考えてみました。

シンプルな問題なので、
そもそもオブジェクト指向で考える必要はないという議論はあると思いますが、
具体的な考え方をお話するために、ここはあえて冗長に考えました。
長文になってしまい、すみません…。


まず、ジャンケンをざっくりと考えてみます。

  • ジャンケンはゲームです。
  • そのゲームには参加者が複数います。
  • 参加者はジャンケンの手を1つ同時に出します。
  • 出した手の強さを競います。
  • 手の強さはグー>チョキ>パー>グー。

これらの情報から抽象的な情報と具体的な情報を切り離しつつ、細かく考えます。
似ている事はまとめます。変わりそうな事は切り離します。

【抽象的な情報】

  • ゲームには参加者が複数必要です。
  • ゲームにはルールが必要です。
  • ゲームは開始されます。
  • 参加者は手を出せます。
  • 出せる手はルールによって決められます。
  • 出した手はルールによって結果が判定されます。
  • 結果は参加者に知らされます。

【具体的な情報】

  • ジャンケンのルール。
    出せる手はグーチョキパーです。
    手の強さはグー>チョキ>パー>グーです。
    強い手を出した参加者は勝ちという結果になります。
    勝ちが決められないときは引き分けという結果になります。
  • (ジャンケンという)ゲームの流れ。
    参加者は同時に手を出します。
    出された手はルールによって勝敗が決められます。
    参加者は勝敗を知ります。
    必要なら勝敗が決まるまで繰り返します。
  • (ジャンケンする)参加者。
    参加者は人間だったりコンピュータだったりします。

ここで、抽象的な情報をインターフェースにします。

  • ゲーム→IGame
  • ルール→IRule
  • 参加者→IPlayer
  • 手(原因)→ICause
  • 結果→IResult

これらの関係を、インターフェースのメソッドで示します。

  • ゲームは参加者が複数必要です。→void IGame.joinPlayers(IPlayer)
  • ゲームにはルールが必要です。→void IGame.followRule(IRule)
  • ゲームは開始されます。→void IGame.start()
  • 参加者は手を出せます。→ICause IPlayer.serveCause()
  • 出せる手はルールによって決められます。→void IRule.valid(ICause)
  • 出した手はルールによって結果が判定されます。→IResult IRule.match(ICause, ICause)
  • 結果は参加者に知らされます。→void IPlayer.result(IResult)

具体的な情報は、これらインターフェースを実装したクラスになります。

  • ジャンケンのルール。→JankenRule
  • (ジャンケンという)ゲームの流れ。→OnceGame、DrawLoopGame
  • (ジャンケンする)参加者。→CauseStrategiedPlayer

ここでは参加者から、人間やコンピュータという具体性を、戦略として抽象化し分離しています。
→JankenCauseAtRandom、JankenCauseStdin

あとはひたすら実装します。
この時、切り離した方が良さそうな事が現れたりしたら、その都度対応します。

今は考慮していませんが、
例えば、3種ジャンケンじゃなくて、5種ジャンケンが必要であれば、
手とルールと戦略のクラスを追加で作ればよいかと思います。ゲームや参加者は再利用できます。
ただ、5種ジャンケンが3種ジャンケンのように「3すくみ」のようなルールであれば、
ジャンケンのルールである「すくみ」から、種類を抽象的な情報として分離すれば、
すくみルールとして再利用できます。


実際に組んだC#のソースです(250行ぐらい)。コンソールアプリケーションとして一応動くはずです。
エラーチェック等はガッツリ省いてます。
行数を削ろうと変な書き方になっているところはご容赦ください…。

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4 5class Program 6{ 7 static void Main(string[] args) 8 { 9 10 IGame game; 11 IRule rule = new JankenRule(); 12 List<IPlayer> players = new List<IPlayer>(){ 13 new CauseStrategiedPlayer(new JankenCauseStdin(), "You") 14 ,new CauseStrategiedPlayer(new JankenCauseAtRandom(), "ComA") 15 ,new CauseStrategiedPlayer(new JankenCauseAtRandom(), "ComB") 16 ,new CauseStrategiedPlayer(new JankenCauseAtRandom(), "ComC") 17 ,new CauseStrategiedPlayer(new JankenCauseAtRandom(), "ComD") 18 }; 19 20 System.Console.WriteLine("--ジャンケン1回--"); 21 game = new OnceGame(); 22 game.followRule(rule); 23 game.joinPlayers(players); 24 game.start(); 25 26 System.Console.WriteLine("--ジャンケン終わるまで--"); 27 game = new DrawLoopGame(new OnceGame()); 28 game.followRule(rule); 29 game.joinPlayers(players); 30 game.start(); 31 32 Console.Write("何かキーを押したら終わります。"); 33 Console.ReadKey(); 34 } 35} 36 37/////////////////////////////インターフェース///////////////////////////// 38 39//ゲームの流れ。 40public interface IGame { 41 void joinPlayers(IEnumerable<IPlayer> players); //ゲームの参加者。 42 void followRule(IRule rule); //ゲームのルール。 43 void start(); //ゲームを開始する。参加者とプレイヤーは登録しておくこと。 44} 45 46//参加者。 47public interface IPlayer { 48 void prepareCause(); //勝負要素を準備する。 49 ICause serveCause(); //勝負要素を出す。prepareするまでは同じ要素が出される。 50 void result(IResult result); //勝負結果。 51 IResult result(); //勝負結果。 52} 53 54//ルール。 55public interface IRule { 56 void valid(ICause cause); //勝負要素が認められるものであるか。NGなら例外を投げる。 57 IResult match(ICause one, IEnumerable<ICause> others); //oneの勝負結果を示す。 58} 59 60//勝負要素(勝負結果の原因)。 61public interface ICause { } 62//勝負結果。 63public interface IResult { } 64 65//勝負要素を出す戦略。 66public interface ICauseStrategy { 67 void prepare(); 68 ICause serve(); 69} 70 71/////////////////////////////クラス///////////////////////////// 72 73//一度だけ行われるゲーム。 74//流れはprepare→serve→match→result。 75public class OnceGame : IGame { 76 private IEnumerable<IPlayer> _players = null; 77 private IRule _rule = null; 78 79 void IGame.joinPlayers(IEnumerable<IPlayer> players) { _players = players; } 80 81 void IGame.followRule(IRule rule) { _rule = rule; } 82 83 void IGame.start() { 84 foreach(IPlayer player in _players) { 85 player.prepareCause(); 86 _rule.valid(player.serveCause()); 87 } 88 foreach (IPlayer one in _players) { 89 IEnumerable<ICause> others = _players 90 .Where(other => other != one) 91 .Select(other => other.serveCause()); 92 one.result(_rule.match(one.serveCause(), others)); 93 System.Console.WriteLine(String.Format("{0} {1} {2}", one, one.serveCause(), one.result())); 94 } 95 } 96} 97 98//OnceGameの流れで、Drawのresultがある場合は繰り返す。 99public class DrawLoopGame : IGame { 100 private IEnumerable<IPlayer> _players = null; 101 private OnceGame _once; 102 103 public DrawLoopGame(OnceGame once) { 104 _once = once; 105 } 106 107 void IGame.joinPlayers(IEnumerable<IPlayer> players) { 108 ((IGame)_once).joinPlayers(players); 109 _players = players; 110 } 111 112 void IGame.followRule(IRule rule) { ((IGame)_once).followRule(rule); } 113 114 void IGame.start() { 115 while (true) { 116 ((IGame)_once).start(); 117 if(_players.Any(player => player.result() is Draw)) { 118 System.Console.WriteLine("--draw--"); 119 continue; 120 } 121 System.Console.WriteLine("--finish--"); 122 break; 123 } 124 } 125} 126 127//戦略に従うだけの参加者。 128public class CauseStrategiedPlayer : IPlayer { 129 private ICauseStrategy _strategy; 130 private string _name; 131 private IResult _result = new None(); 132 private ICause _served = null; 133 134 public CauseStrategiedPlayer(ICauseStrategy strategy, string name) { 135 _strategy = strategy; 136 _name = name; 137 } 138 139 IResult IPlayer.result() { return _result; } 140 void IPlayer.result(IResult result) { _result = result; } 141 142 void IPlayer.prepareCause() { 143 _served = null; 144 _strategy.prepare(); 145 } 146 147 ICause IPlayer.serveCause() { 148 if ( _served == null) { 149 _served = _strategy.serve(); 150 } 151 return _served; 152 } 153 154 public override string ToString() { return _name; } 155} 156 157//ランダムにジャンケンの勝負要素(手)を出す戦略。 158public class JankenCauseAtRandom : ICauseStrategy { 159 160 private static int _seed = 0; 161 private Random _random; 162 163 private List<ICause> _causes = new List<ICause>() { 164 new Guu(), new Choki(), new Paa() 165 }; 166 167 public JankenCauseAtRandom() { 168 //同時に複数生成した場合に、乱数シードが同じになってしまうのを避けるため 169 _seed += Environment.TickCount; 170 _random = new Random(Environment.TickCount + _seed); 171 } 172 173 void ICauseStrategy.prepare() { } 174 ICause ICauseStrategy.serve() { 175 return _causes[_random.Next(0, _causes.Count())]; 176 } 177} 178 179//標準入力でジャンケンの勝負要素(手)を決める戦略。 180public class JankenCauseStdin : ICauseStrategy { 181 void ICauseStrategy.prepare() { } 182 183 ICause ICauseStrategy.serve() { 184 while (true) { 185 Console.Write("グー[g] チョキ[c] パー[p]のどれかのキーを押して[enter]して下さい >"); 186 string input = Console.ReadLine(); 187 if (input.Equals("g")) { return new Guu(); } 188 if (input.Equals("c")) { return new Choki(); } 189 if (input.Equals("p")) { return new Paa(); } 190 } 191 } 192} 193 194//ジャンケンのルール。 195public class JankenRule : IRule { 196 private Dictionary<Type, Type> _win_pattern = new Dictionary<Type, Type>() { 197 { typeof(Guu), typeof(Choki) } 198 ,{ typeof(Choki), typeof(Paa) } 199 ,{ typeof(Paa), typeof(Guu) } 200 }; 201 202 private Dictionary<Type, Type> _lose_pattern = new Dictionary<Type, Type>() { 203 { typeof(Guu), typeof(Paa) } 204 ,{ typeof(Choki), typeof(Guu) } 205 ,{ typeof(Paa), typeof(Choki) } 206 }; 207 208 void IRule.valid(ICause cause) { 209 if (cause is Guu || cause is Choki || cause is Paa) { 210 return; 211 } 212 throw new Exception(String.Format("{0}は認められない。", cause)); 213 } 214 215 IResult IRule.match(ICause one, IEnumerable<ICause> others) { 216 Type one_type = one.GetType(); 217 bool win = others.Any(other => _win_pattern[one_type].Equals(other.GetType())); 218 bool lose = others.Any(other => _lose_pattern[one_type].Equals(other.GetType())); 219 bool draw = others.Any(other => one_type.Equals(other.GetType())); 220 if (win && lose) { return new Draw(); } 221 if (win && draw) { return new Win(); } 222 if (lose && draw) { return new Lose(); } 223 if (win) { return new Win(); } 224 if (lose) { return new Lose(); } 225 return new Draw(); 226 } 227} 228 229public class Guu : ICause { 230 public override string ToString() { return "グー"; } 231} 232public class Choki : ICause { 233 public override string ToString() { return "チョキ"; } 234} 235public class Paa : ICause { 236 public override string ToString() { return "パー"; } 237} 238 239public class Win : IResult { 240 public override string ToString() { return "勝ち"; } 241} 242public class Lose : IResult { 243 public override string ToString() { return "負け"; } 244} 245public class Draw : IResult { 246 public override string ToString() { return "引き分け"; } 247} 248public class None : IResult { 249 public override string ToString() { return "結果なし"; } 250}

投稿2016/05/09 04:07

編集2017/03/29 00:07
i50

総合スコア227

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

クラスを作成する際に、どのような考え方でクラスを作成してますでしょうか?

経験上、プログラムを設計する際、可能な範囲でリアル世界と同じ構造にしておくと、スムーズに開発できます。リアルとプログラムが似た構造であれば、リアル上の要求の変化をプログラムへ反映しやすく、将来の拡張も容易なことが多いです。

つまり、リアルに存在する「オブジェクト」の性質をプログラムの「クラス」で表現し、リアル・オブジェクトが存在する姿に合わせて、クラスのインスタンスを生成するようなプログラムにしています。


じゃんけんゲームのような場合、たぶん、プレイヤー・クラスを基底クラスとし、自分クラスやコンピュータ・クラスを派生させるでしょう。ゲームの仕様によってはネットの向こうにいる対戦相手クラスを派生させることもあるかも知れません。
実際のじゃんけんでは普通は審判を各プレイヤーが担いますが、リアルの場合でも矛盾しない判定(後出し等の判定)を必要とするなら審判を別途設けることもあります。それと同様に審判機能を担う「ゲームマスター」クラスを作るだろうと思います。(その方が簡単ですし、各プレイヤーが判定することで、場合によっては喧嘩するところまでモデル化する必要はまずないですから。)
その「ゲームマスター」クラスが、各基底クラスのプレイヤー・クラスとI/Fしてじゃんけんゲームを進行させる感じですね。
もし、結果を記録に残す必要があるようなら、記録係の仕事を担うログ・クラスを作ります。

さて、「結果」はリアルでも存在しますが、「結果」が主体的に動いて何かをなすことはないですね。ですので、結果クラスは作らないだろうと思います。
なお、結果が複数の値を持っている場合は、結果構造体にてやり取りすると思います。リアルの場合は記録紙に当たるイメージです。
構造体は、メンバ変数は原則publicでコンストラクタ以外のメソッドを積極的に持たせることはないようなものをイメージしてます。オブジェクト指向の「オフジェクト」には当たらないですが、必要に応じて設計します。

投稿2016/05/06 06:31

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2016/05/06 10:22

構造体とクラスって区別する必要あるんでしょうか?C++だとデフォルトのアクセス制限以外全く同じですし、C#だと値型であるという違いがあるけど普通にメソッド定義できますし、結局、それってクラスの一種と言ってもいいものなんでは…
Chironian

2016/05/06 13:34

raccyさん。 構造体とクラスは言語仕様的に事実上同じ言語もありますが、この2つは使用目的が異なると考えています。 構造体はどちらかというとデータを保持することを目的とし、自律的に機能を提供するというよりは、受動的にデータ保持を行うイメージです。C言語の構造体をイメージして頂ければぴったりです。 メッセージをやり取りして動作する、派生して機能を拡張する、ポリモーフィックに使う等オブジェクト指向的に使う時に、「構造体」はちょっと違うかなと思います。 このような使い分けをしている人は少なくないようです。(https://teratail.com/questions/21279) raccyさん自身からも、「構造体は全く別枠で考えています。」とのご意見を頂いてますよ。
raccy

2016/05/06 13:50

> Chironianさん そ、そ、そのraccyはきっと偽者に違いないっす(-д-; いや、俺が偽者という可能性も… まぁ、私も構造体は使用目的が違うってのは同意です。私もC++でstructって書くときはCのように使うときしか使いませんから… 今回の主題になっている質問を「クラス」=「型」という感じで私の方が捉えちゃったので、ちょっと勘違いかも知れません。オブジェクト指向って、別に全てのクラスにメソッドがある必要も、多態性がある必要も無くて、オブジェクト中心ならオブジェクト指向なんじゃ無いか、むしろ、全部オブジェクトとして考えた方が楽なんじゃ無いかと思ったりしています(きっとRubyに洗脳されたせいだと思われます)。クラスを広い意味で、つまり型という意味でとらえたら、全部クラスと言ってもいいんじゃないかと。でも、そうしたら、クラス云々を話す必要がなくなりますね。あれ、何言っているんだろ?
Chironian

2016/05/06 14:28

> そ、そ、そのraccyはきっと偽者に違いないっす(-д-; > いや、俺が偽者という可能性も… 偽者が多すぎて誰が本物か判らなくなってしまったようですね。 なるほど、さすがraccyさん。奥が深い。
guest

0

データの取り扱いという側面から考えた場合、カプセル化という考え方からクラスを切り出せると思います。
また処理のありようとしてはデザインパターンという考え方から処理の抽象化や実装のありようを探ることもできそうです。

例えば、じゃんけんゲームを作成するとして

私の場合ならPlayerクラス、Refereeクラスで作成すると思います。

java

1class Player{ 2 public static final int ROCK = 0; 3 public static final int PAPER = 1; 4 public static final int SCISSORS = 2; 5 private int rps = -1; 6 public Player(int rps){ 7 // 自分で考えた手でPlayerインスタンスを初期化 8 } 9 public void makeAGuess(){ 10 // ランダムで手を考えるメソッド(PC側が使用) 11 } 12 public int open(){ 13 // 手を公開するメソッド 14 return this.rps; 15 } 16} 17class Referee{ 18 public static void main(String[] args){ 19 int input = -1; 20 // 標準入力から「私」の手を入力 21 Player a = new Player(input); 22 // 「相手」の手をランダムで生成 23 Player b = new Player(0); 24 b.makeAGuess(); 25 // 判定 26 Referee r = new Referee(); 27 Player winner = r.getWinner(a, b); 28 if(winner == null){ /* draw! */ } 29 if(winner.equals(a)){ /* you win! */ } 30 else{ /* you lose! */ } 31 } 32 public Player getWinner(Player a, Player b){ 33 int result = (a.rps + 3 - b.rps) % 3; 34 if(result == 1) return a; 35 if(result == 2) return b; 36 return null; 37 } 38}

冒頭の例でいえば「カプセル化」を考えた書き方ですね。

投稿2016/05/06 02:09

tkturbo

総合スコア5572

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

じゃんけんといっても簡単でない気がします。
いきなり相手クラス・自分クラスでしょうか?

たぶん、ゲームの場(2人以上参加できる場)、プレーヤー(意思決定主体)、ゲーム(参加者全員の勝敗・順位が決まる単位)、じゃんけん(一回一斉に出す勝負)、でその他ディーラーのようなコントロールクラスが必要かもしれません。プレーヤーがじゃんけんで出す手を決める方法が自分だったりネット上の誰かだったり、ボットだったりなんじゃないでしょうか。
じゃんけんのルールとしてグーチョキパーの種類を増やした場合と、それらの組み合わせで勝敗を決める審判のクラスも用意するかもしれません。(5種とか7種のじゃんけんとか世界にはあります。)

柔軟性を確保しつつ、変えた部分が他のクラスへの影響が少ないようにカプセル化していきます。
グーチョキパーが3種なのか5種なのかは、何勝何敗なのかとは関係ないわけですよね。

企業のシステムはむしろゲームより簡単だったりして、在庫だとか注文だとかエンティティの単位は、業務のドキュメントで規定されているので、それに従うことが多いかと思います。

投稿2016/05/06 01:36

編集2016/05/06 01:48
thesecret11

総合スコア234

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まず、大きめの紙かホワイトボードに、作りたいものを説明するときに登場する思いつく限りの単語を書き出します。(これれらをビジネスドメイン用語という)
このうち、何かの名前であるものはすべてオブジェクトの候補です。
オブジェクト名をよく眺めると似たもの同士で集めたグループができます。
そのグループを表す名前があれば、それはクラスの候補です。
グループのメンバーにさらに役割や動作の異なる種類がりそれに名前があれば、それはサブクラスの候補です。
グループのメンバーが状態や種別を表すのなら、クラスの代わりに列挙(enum)できるかもしれません。
クラスの集まり眺めるとまた性質や扱いに似た者同士が集まります。
そのクラスの集まりに名前が付けられたら、○○的なものという抽象クラス(あるいはインターフェース)の候補です。
単語のうち、何らかの動詞であるものはオブジェクトの機能でありメソッドの候補です。
メソッド名をよく眺めると関連するもの同士を集めたグループができます。
そのグループを表す名前があれば、○○するものというインターフェースの候補です。
余った単語のうち、あるオブジェクトの一部や状態のことだったら、それはプロパティの候補です。
この時点でプロパティは出し切れません。
同じクラスのオブジェクトの集まりを管理するオブジェクトが必要になるでしょう。
そのためのクラスを定義してもいいですが、とりあえずありもののコレクションライブラリでいいでしょう。
最後にこれらのクラスを作りたかったビジネスロジックを実装するアプリケーションクラスを作ります。
しかしこれはなんとかというアンチパターンで実装後ほどなく破たんします。
そうならないようもう一度書き出して、各単語間の関係を線や丸囲みで整理します。
その線や丸がなるべく階層性を持つように整理します。
依存性の矢印がなるべく重なったりループにならないよう整理します。
クラスを分けたりくっつけたり移動したり消したりインターフェースを切ったりつぶしたりします。
よく見ると、結合度によって境界と島ができます。
この島も階層性を表すように整理します。
この島と島を結ぶ線をなるべく少なくなるように整理します。
依存性が逆流している線をなるべく少なくなるように整理します。
島と島を突き抜けた矢印がなるべく少なくなるように整理します。
この整理で悩んだところが、デザインパターンの候補です。
この島の構成をなぜこうしたのか説明できる言葉を持つことが、人にわかってもらう気もためのキモです。
島のレイヤーが増えて境界に晒すインターフェースをたくさんきるでしょう。
オブジェクト間の関連もまたオブジェクトであることに気づきクラスとインターフェースを切るでしょう。
複雑なオブジェクトの生成方法に悩むでしょう。
オブジェクトの責務が上に行ったり下に行ったりするでしょう。
ビジネスドメイン以外のクラスも必要ですが、これらはとりあえずありもののライブラリやフレームワークを試します。
何日か経ちます。
ファイルやDBといった外部リソースはありもののライブラリやフレームワークを試します。
何日か経ちます
各クラスのテストクラスもそろそろ用意します。
設定クラス、定数クラス、例外クラス、ロガーなど、今は使わなくても追加しておきます。
アプリケーションコンテナの規約にそった実装も必要になります。
これは何時でもすぐにスタートアップできるテンプレートを用意しておきまましょう。
実装を開始します。
うかつな設計に気づきます。
あるオブジェクトがある特殊な状態を表現できないことに気づきます。
あるオブジェクトを取り出すのが異常に面倒くさいことに気づきます。
あるオブジェクトが多対多でなければならなかったことに気づきます。
認証やセキュリティ対策を後から入れるのは大変なことに気づきます。
ライブラリやフレームワークの選択に間違えたことに気づきます。
設計破りの穴をあける決断を迫られミジメな気持ちになります。
自分の愚かさとうぬぼれに気づきのたうちまわります。
以下略・・・

投稿2016/06/11 13:52

編集2016/06/11 14:01
kumazo

総合スコア12

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

適当に書いてそれで動くようにクラスを設計します。
見通しよく、使いやすいのが良いクラスなので・・・

例えばコンソールで作るとして、こんなゲームをイメージします。

Let's Play 名前は? foo どれを出す? 1:グー 2:チョキ 3:パー 0:終了 1 fooさん: グー 相手: チョキ fooさんの勝ち スコア fooさんの勝ち: 1 回 引き分け: 0 回 相手の勝ち: 0 回 どれを出す? 1:グー 2:チョキ 3:パー 0:終了 0 Game End スコア fooさんの勝ち: 1 回 引き分け: 0 回 相手の勝ち: 0 回

テキトーにメインを書きます。

javascript

1var p1 = new Player(GetName()); 2var p2 = new RobotPlayer(); 3 4var score = new Score(p1, p2); 5Console.log("Let's Play"); 6 7var result = score.Play(p1, p2); 8while(result.AtEnd()){ 9 ShowResult(result); 10 ShowScore(score); 11 12 result = score.Play(); 13} 14Console.log("Game End"); 15ShowScore(score);

これに合うクラスであればとりあえずオッケー。

投稿2016/05/11 06:53

編集2016/05/11 07:01
iwamoto_takaaki

総合スコア2883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

iwamoto_takaaki

2016/06/12 14:50

大きいプログラムについて言及していませんでしたね。大きいプログラムでもおんなじです。 「ユーザーはこう使いたいよなー」「こうゆうオブジェクトの呼び出ししたいよなー」「ここはこう書いたほうが読みやすいよなー」といった。やりたいこと順に決めていきます。 責任の分担をして、上位のオブジェクトは下位のオブジェクトについて無関心であるのが流儀だと思います。したがって、上位のオブジェクトから設計するのが基本よい設計につながります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問