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

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

ただいまの
回答率

88.10%

オブジェクト指向で料理を例える場合,chicken.cut()かchef.cut()か

解決済

回答 13

投稿 編集

  • 評価
  • クリップ 25
  • VIEW 11K+

score 109

実用的で無い内容なのでここで聞いて良いのか悩みましたが,一応クラスの設計の参考になるだろうと思って質問しました。
自分の普段使いがPythonなので,説明用のコードはPython風ですが,どの言語にも共通する問題だと思います。

先ほど友人と,「鶏肉を5つに切る」にはどうすれば良いかという例えばなしをしていました。
これを実装する場合,

  • chicken.cut(5)  # chickenは (Foodクラスを継承した) Meatクラスのインスタンス
  • chef.cut("chicken", 5)  # chefはChefクラスのインスタンス。あらかじめset_foodなどで{"chicken": chicken}を内部に保持した状態にしておく

の2通りが考えられるのではないかと思います。

鶏肉の状態が変化するという意味では,「chickenが5つに分割された状態になる」前者の方が自然であるように思えます。それに説明が短くて済みます。おまけに「オブジェクト指向 料理」で検索するとこっちの例え話しか出てきません。議論のきっかけになった第三者の意見も,「鶏肉.cut(5)できたらいいのに」というものでした。

しかし一方,cutするのは料理人であることを踏まえると,「chefがchickenを5つにcutする」方が自然です。しかも,

  • 食材chickenとeggを混ぜる → chef.mix("chicken", "egg")
  • 手持ちの食材を見せる → chef.show_foods()
  • 親子丼のレシピを覚える → chef.learn_menu("oyakodon", [needed_foods])
  • 食材chickenとeggを消費して料理oyakodonを完成させる → chef.cook("oyakodon")
  • 熟練度システムを加える → 「if self.level >= 10」などを加える
  • 「knife」が無いとcutできないことにする → 「if self.has_knife」などを加える

と,後々の拡張が容易です。なので,僕はこちらの案に一票を入れました (とはいえ,Chefクラスを一体どれほど大規模なクラスにするつもりなのでしょうか?)

他にも,料理は料理で別クラスにすべきだ,とか,オブジェクト指向が本当に必要なのか,とか,初心者なりに様々な意見が出ました。

これらについて,オブジェクト指向に慣れた方の意見を聞きたいです。
よろしくお願いします。


補足: 僕が想定していた設計例はこんな感じです

class Food(object):
    def __init__(self, size, hardness):
        self.size = size
        self.hardness = hardness


class Chef(object):
    def __init__(self, level):
        self.level = level
        self.has_knife = False
        self.foods_ = {}

    def set_food(self, food, obj, n):
        self.foods_[food] = [obj, n]
        print('%sを手に入れた!' % (food))

    def show_foods(self):
        foods = self.foods_
        return {i: (foods[i][0].size, foods[i][1]) for i in foods.keys()}

    def cut(self, food, n):
        if food not in self.foods_.keys():
            print('食材がありません')
        elif not self.has_knife:
            print('ナイフがないと切れません')
        elif self.level < self.foods_[food][0].hardness:
            print('レベルがたりません')
        else:
            self.foods_[food][0].size *= 1 / n
            self.foods_[food][1] *= n
            print('%sを%d個に切断しました' % (food, n))

meat = Food(size=10, hardness=3)
chef = Chef(level=1)
chef.cut('meat', 5)  # 食材がありません
chef.show_foods()  # {}
chef.set_food('meat', meat, 3)  # 'meatを手に入れた!'
chef.show_foods()  # {'meat': (10, 3)}
chef.cut('meat', 5)  # ナイフがないと切れません
chef.has_knife = True
chef.cut('meat', 5)  # レベルがたりません
chef.level += 10
chef.cut('meat', 5)  # meatを5個に切断しました
chef.show_foods()  # {'meat': (2.0, 15)}

皆さん多くの有用な回答をありがとうございました。
当初の質問「foodが切るかchefが切るか」について,food派の回答で最も納得のいく理由を説明してくださったLLmanさんをベストアンサーとさせていただきました。
(全員へ返信する時間がなかったのでLLmanさんへの返信欄に全員宛のお礼を書かせていただきました)

今回知ったことを実際のコーディングにも活かせていければと思っております。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2016/07/22 09:34

    こちらの質問が他のユーザから「やってほしいことだけを記載した丸投げの質問」という指摘を受けました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 13

+17

少し醒めた意見ですが、「正解など存在しない」という回答をさせてもらいます。

オブジェクト指向設計が目指しているのは「現実世界をそのままプログラム設計に射影(マッピング)すること」ではなく、そのプログラムの目的に合わせて「真に解決すべき課題を分析し、オブジェクトという単位を用いてモデル化(表現)すること」です。

初学者向けの説明では、動物クラスから犬猫サブクラスを派生してみたり、車オブジェクトに4つのタイヤインスタンスを持たせたりるすのが人気ですが、それらはオブジェクト指向の説明用に作られたオモチャにすぎません。

ほそく:冷たい回答ではありますが、質問者さんの議論自体が無駄という否定意図はありません。むしろ「オブジェクト指向が本当に必要なのか」という視点の方が重要かと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 21:36

    現状の回答の中ではこちらの意見に最も賛同します。
    回答が定まらないのは、問題が曖昧だからです。曖昧な問題には曖昧な解決策しか存在しません。
    いろいろな回答をされる方がいらっしゃいますが、「この問題を、あなたはそう解釈したんですね。私は違う解釈をしました。」ということをそれぞれの人がした結果に過ぎません。
    プログラミングとは、抽象的で曖昧な問題を、より具体的な問題に変換するという作業を、究極の具体的実体(プログラミングの場合、ソースコード)が得られるまで繰り返すという仕事に他なりません。
    具体的な解決策は、問題を具体化するという意思決定を通して初めて得られるものだと考えます。
    オブジェクト指向というのは、その中の過程をどう進むべきかという道筋の一つに過ぎません。

    キャンセル

checkベストアンサー

+9

切るメソッドは料理人が持つか、(鶏)肉が持つか、というご質問。
結論から言いますと、多くの場合で「」側が持った方が良いです。

なぜでしょうか? 料理人不在で肉が自分で勝手に切れるはずないと。
しかしこれは、よくある言語の標準ライブラリを見れば分かります。

たとえば「split」メソッドはいろいろな言語にあります。
Rubyの場合はStringクラスのメソッドになります。
ここで文字列は、自分で勝手に切れていることになります。


なぜ現実での受動側に持たすかというと、料理人側に持たすと、
クラスが増えて複雑化するか、料理人クラスが肥大化するからです。

クラスがひとつで完結するなら、もっとも高凝集・疎結合ですし、
料理人クラスがある前提でも、けっきょく責務の分散が必要です。

ご質問のソースを見ても、「Food」クラスに対して「Chef」クラスが
圧倒的に大きくなっていますし、Chefのメソッドだけ使われています。

忠実に現実に合わせると、肉は自分から能動的に何もできないので、
「Food」はメソッドを何も持たず、ただのデータの入れ物になります。

しかしそもそも、データと処理を一体化するのがオブジェクト指向ですし、
メッセージングによる責務の協調分散システムがオブジェクト指向です。
料理人の中央集権では、オブジェクト指向の本筋から外れてしまいます。


もし物が勝手に動作する非現実的な事態が納得できないならば、
ここでの料理人は日本の伝統芸能の「黒子」だと思ってください。
つまり黒子ではなく、操られている人形の方に関心があると。

あるいは政治や歴史の話で「国が分裂した」というとき、
本当はその国の人間たちが主体となって分裂させたのだけれど、
国を単位にした方が話が分かりやすいから、国を擬人化した言い方をします。

そのようにオブジェクト指向では、オブジェクトを擬人化する見方があります。
デザインパターンにも「メディエータ(仲介者)」「オブザーバ(監視者)」
「ビジター(訪問者)」などで擬人化が見られますね。

オブジェクト指向(分析・設計)では、現実をそのまま写実するのではなく、
といって無視するのでもなく、使用目的に沿った形で現実を物語として見ます。

物語ですからたとえば、「ナイフクラスが切る動作を担当する」という形で、
調理人から調理器具へ調理行為を委譲しても良いのです。
こうするとクラス単体が肥大化せずに、拡張も容易です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/24 15:49

    皆さん色々な意見をありがとうございました。
    今回,回答を大別すると (細かい意見の違いを無視すれば),

    * Chefがcutすべき ... 現実に沿っているから。分かりやすいから
    * Foodがcutすべき ... Chefクラスの肥大化を防ぐため
    * ChefのcutによってFoodの内部状態を変更するべき
    * 追加のクラスを作成すべき
    * 課題解決を最優先してどちらにcutを実装するか決めるべき

    の5つががありました。全ての意見が参考になるものでした。

    今回はChefとFoodどちらが切るかという話であり,また自分はChefが切るほうが良いと当初考えていました (実は「どう考えてもChefが切ったほうがいいじゃないか」ぐらいに思っていました)。そいうった理由で,「Foodにcutを実装する派」の中で最も納得できる理由を説明してくださったLLmanさんをBAにさせていただくことにしました。

    多数の賛同を得ていたyohhoyさんをはじめとする数人のおっしゃる通り,Chefが切るかFoodが切るか問題は,「直面した課題ではどちらで実装しても問題なさそうだ」となってから初めて考えれば良いものなのだと思います (今回,上級者間ですら意見が割れた理由は,過去のコーディング経験の中で直面してきた問題がそれぞれ異なるからなのかも)。とはいえ,課題がどちらの実装を推奨するかを十分に選んでくれない時もあるかもしれません。そうなった時に,分かりやすさのためにChefが切ることにしよう,クラスの大きさのバランスを考えてFoodが切ることにしよう,のような,ここで得た意見を参考にしていきたいと思います。

    ありがとうございました。

    キャンセル

+4

そんなに慣れているわけでなく感覚的な回答ですが、日本語で書くと、

  • シェフは「切る」
  • 鶏肉は「切られる」

の意味でどちらも cut なんだろうと思います。
設計上、

  • シェフに「鶏肉を切れ」という :切る対象を指定する
  • 鶏肉はシェフに「切られる」 :切られた際のふるまいを定義する

と考えられるでしょう。
そのため、chef.cut(chicken) は内部処理として chicken.cut() を実行するでしょうし、その戻り値は「切られた鶏肉」という別のオブジェクト(のコレクション)でしょう。

おそらく、同じMeatクラスを継承して、

  • chicken:鶏肉(丸)
  • cut_chicken:鶏肉(切られた後)

が存在し、

chef.cut(chicken) でchef内に一時的に鶏肉(丸)が保持され、cutメソッドによりchicken.cut()が実行されその戻り値としてcut_chickenが複数(コレクションとして)戻ってくる、それをchefオブジェクト内で管理する、となるのではないでしょうか。

おそらく、chefオブジェクト内では

  • 調理器具
  • 食材
  • レシピ

といったものをコレクションとして管理し、chef.cook(レシピ名) でレシピオブジェクトに適正な在庫を与え、在庫を減らし、レシピオブジェクトの戻り値を「料理」として提供するように設計すると汎用的じゃないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

まさに私も同じ質問を昨晩からしたいと考えていたところだったので、すごいタイムリーで驚きました!

昨日読んでいたアジャイルソフトウェア開発の奥義という名著の中でも、employee.pay()のようなコードがあり、「支払うのは雇用者であるemployerであって、従業員であるemployeeじゃないだろ!」と1人でツッコミいれてました。

他にも先日見かけたコードでは、vendingMachine.buy()のようなコードがあり、「購入するのは自販機の利用者であって自販機じゃねーだろ!」と、腑に落ちないコードに悶々としていました。

個人的にはやはり、メソッドを持つオブジェクトが主語であった方が自然なので、vendingMachine.buy()よりはvendingMachine.transactのような動詞の方が自然だと感じますし、employee.pay()よりはemployee.getPaidの方が自然に感じます。

ただ、これだと単に命名の話ということになってしまうのですが、結局、どういった設計がより適しているかはケースバイケースなのでこうとしか言いようがありません。chiken自体に持たせるのであれば、やはり前述の通り、cutだと意味的に不自然感が半端ないので、chickenが保持する内部データの変化を表すような名前でメソッド名を考えてあげると良いのかなと思います。例えば、chicken.split()とか。これであれば、chef.serve("親子丼")というメソッドを実行した時にchefの内部で持つknifeクラスのcutメソッドが呼ばれて、さらにcutの対象であるchicken.split()が実行され〜〜〜のようなことで意味的にも自然だと思います。

先に挙げた例に戻ると、employee.pay()のような名前だと従業員が立て替えしておいたようにも解釈出来ますし、罰金でも支払わされたのかとも解釈出来てしまいます。混乱の元になると思うので、「〜された」ことをメソッドとするならば、やはり自然に意味が通る名前にすべきだと思います。

この質問、他の方の回答がすごい気になります!

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

「真に解決すべき課題を分析し、オブジェクトという単位を用いてモデル化(表現)すること」という意見に賛同します。

とりあえずの回答をすると、切るのは調理担当者であり、シェフは厨房担当社の派生クラスです。切るのは刃物によってであり、鶏肉には切られる実装が必要です。また、鶏肉は様々な状態をもっており、調理担当者は状態を確認しつつ、切り方または切らないという判断をします。このとき、切った状態の鶏肉も鶏肉の状態が変わっただけなので、分けて考える必要はありません。といったところでしょうか。

ここから先は質問の内容からはずれます。

現実の物事は沢山の情報からできています。今回の例であれば、これをまとめて、切った鶏肉を5つ用意するという機能にまとめます。調理担当者はどのような方法かは別にして、切った鶏肉が用意できるのです。

ここがオブジェクト指向の良いところで、モデリングと呼ばれます。モデリングとは、「細かい点はほっといて大括りにする」という意味です。(それこそかなり大括りな表現です。)

そして、これを業務分析に使うのか、発注の自動化をしたいのか、調理の自動化がしたいのか、シミュレーションをしたいのかによってどのようにモデリングするかが違ってきます。関心事によって、とらえ方が変わるので、モデルも変わるということです。

また、コーディングにおいて使われるオブジェクト指向は、オブジェクト指向の本質には関係ないプログラミングテクニックがほとんどを占めます。なので、オブジェクト指向プログラミングとオブジェクト指向は分けて考える必要があります。

とはいえ、個人的には実はオブジェクト指向プログラミングから先に学んだほうが分かりやすいのではないかと思います。抽象化の訓練ができるので、考え方が理解しやすいからです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

思うがママに作ってみました。

import math
from itertools import chain

class ZeroWeightError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class UndividableError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class Chicken:
    """
    鶏肉
    """
    def __init__(self, weight):
        """
        重さ。なお、小数点数以下は切り捨てられる。もったいない!
        """
        self.weight = math.floor(weight)
        # 質量 0 のチキンは存在しない。
        if self.weight == 0:
            raise RuntimeError('質量 0 のチキン!')

    def divide(self, ratio = 1):
        """
        ratio は割合、1なら1:1、nなら1:n
        """
        try:
            return [Chicken(self.weight * x / (1 + ratio)) for x in [1, ratio]]
        except ZeroWeightError:
            raise UndividableError('もうわけれまへん')

    def join(self, other):
        """
        やばい、くっつけよう
        """
        return Chicken(self.weight + other.weight)

    def __str__(self):
        return 'Chicken[weight: %d]' % self.weight


class Chef:
    """
    自称一流シェフ(本当は三流)
    """
    def cut(self, target, number):
        target_list = [target]
        while len(target_list) != number:
            if len(target_list) < number:
                # とりあえず分割すれば良いんじゃ寝?
                target_list = list(chain.from_iterable(
                        [x.divide() for x in target_list]))
            else:
                # あれ、分けすぎた。最初の方はくっつけよう
                target_list = [target_list[0].join(target_list[1])] + \
                        target_list[2:]
        return target_list

class SuperChef:
    """
    本物の一流シェフ
    """
    def cut(self, target, number):
        target_list = []
        for i in range(1, number):
            [divided_target, target] = target.divide(number - i)
            target_list.append(divided_target)
        target_list.append(target)
        return target_list

chicken1 = Chicken(500)
chicken2 = Chicken(500)

print('自称天才料理人のチキン')
yumei_chef = Chef()
chicken_list1 = yumei_chef.cut(chicken1, 5)
for c in chicken_list1:
    print(c)

print('三つ星料理人のチキン')
cho_yumei_chef = SuperChef()
chicken_list2 = cho_yumei_chef.cut(chicken2, 5)
for c in chicken_list2:
    print(c)

「5つにカットする」とひとことに言ってもカットの仕方は色々です。はたして、鶏肉はそれを知っているのか?否、鶏肉は知りません。鶏肉は指示された通りに分割されるだけです。なので、鶏肉は分割(divide)関数しかありません。5つの分けられかたは知らないのです。では知っているのは誰か?そう、もちろん料理人です。でも、料理人も三流から超一流までさまざまです。料理人によって「5つにカットする」も違ってきます。それを再現したコードになっています。まぁ、最初の料理人はなんというか…。なお、鶏肉はなんとなくimmutable風になっていますので、分割すると増殖しているような気がしないでもないです。

このコードの指針は「責任分岐点」をはっきりすることです。鶏肉の責任は分割されるところまでです。大きな事はできません、指示された割合通りに二つに分かれるだけです。で、実際カットするのはシェフへおまかせとなっています。シェフの腕によって別れ方も様々です。分け方を工夫するのはシェフの仕事です。鶏肉の仕事ではありません。でも、分けられて重量が減っていくのは鶏肉の仕事です。もしかしたら、二つに分けても重さが変わらない夢の鶏肉を材料に使っていたら、鶏肉は重量が変わりません。逆に、分割するときに無駄肉が出まくって、常に1割の肉が駄目になるかも知れません。つまり、重量が変わるかどうかはシェフの仕事ではありません。

こうすると何が良いのでしょうか?それは、シェフをコロコロ変えられると言うことです。仕事が速いが雑なシェフ、丁寧だけど遅いシェフ、そんな二種類のシェフを使い分けることができます。そして、材料も同じようにdivideやjoinがあれば、鶏肉である必要もありません。中にはdivideできない材料もあるかも知れませんが、それこそエラーになれば良いのです。

これらは考え方の一つに過ぎません。ただ、オブジェクト指向において各クラスの「責任分岐点」をはっきりし、全体の疎結合を維持することは重要な要素と思っています。という感じですが、いかがでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

完全な正解は無いと思いますが、food側にcutを持たせるケースが多いのではないかと思います。
何故なら多くの場合、肉の切断という加工は調理の一工程に過ぎないので、後続の処理のために状態を保存することになると思うからです。
食材のインスタンスがcut処理をされた直後に破棄されたり(代わりに「カットされた鶏肉」のインスタンスをchefのプロパティに持たせる)、foodだけを見てもcut済みかどうか判別できなくても良ければ問題ないのですが、そうでなければcutされたというステータスを食材側で維持する必要が出てきます。
多くの場合、インスタンスのプロパティは隠蔽することがメンテナンス性の向上につながると思いますので、別インスタンス(chef)のメソッドから食材(food)のプロパティの変更をしなければいけない設計が窮屈になるかも知れません。
※食材側にステータスを変更するためだけの(cutではない)メソッドを追加してもいいですが...

それで、chef側にもcutかそれに近いメソッド(調理の前の加工)を作って、その中でfoodのcut(調理されることによるステータス変化)を実行すればすっきりするのでは無いでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

こんにちは。

オブジェクト指向というか、コンピュータ・プログラムの1手法を説明することを目的とした場合、その例は的確ではない印象を受けます。

そもそもchicken.cut(5)の戻り値はなんでしょう? chickenインスタンスが5個とは思いますがlistに入れて返すのでしょうか?
そんな基本的な部分を曖昧にしているので、いざプログラムを書こうとした時に障害になりそうな印象を受けます。(既に知っている人への説明なら気にする必要はないと思いますが、比較的基礎に近いオブジェクト指向の説明なので初心者さん向けでしょう。)

また、現実世界でも鶏肉が自ら5片に切れるというのはあり得ないです。その現実からかけ離れたプログラムは人間であるプログラマにとって理解が難しいです。
更に、あり得ない姿でモデル化されたプログラムは、現実世界で発生する仕様変更にうまく追従できない可能性が高いと思います。
そして、両方が相まって無理っと更にあり得ないクラスを導入して謎化するのではないでしょうか?

chickenクラスがweight変数(例えば単位はグラム)を持っていて、chefがcutメソッドでそれを2つに切り分けるとすっきりしそうな印象です。(5つに切る際には4回適用する。)
そして、関数テンプレートやポリモーフィズムを用いればchiken以外のオブジェクトも処理できますので、それほど巨大化しないで済むかも。

オブジェクト指向を良く知っている人の中には、構造体の概念を許さず、クラス(原則フィールドはprivateでメソッド経由でのみ操作する)しか有ってはならないと言う考え方の人がいます。そのような人ならchefだけでなくchikenにもcutメソッドを実装して、現実離れしているだけでなく、よく似た微妙に異なるコード(cut)をいくつも書くと思います。
しかし、chikenを構造体(全てのフィールドがpublicなクラス)として設計すれば、スマートに現実をモデル化できると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

"シェフが肉を切る" の表現は
chef.cut(chicken)
chicken.cut_by(chef)
のどちらでも構わないとおもいます。

chef.cut(chicken) の返り値は、 chef 自身か Status 値になるでしょう。
chicken.cut_by(chef) 返り値は、 cut された肉のセットか Status 値になるでしょう。

料理人はメッセージを食材に送り、 食材はメッセージを受けて状態が変化していく。
システム構築を、どの視点で組み立てていくかを明確にして、記述を統一することが一番重要と思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-2

Chef と Chicken の関係を考えて、Chickenが親クラスであるFoodを実装したものだとして、

Chef は Foodを実装したインスタンスを1つ受け取った後に、自身のcut()を実行すると、大きさの異なる複数のFoodを実装したインスタンスを生成する

単純ではありますが、Chefが制御(Controller)で良いのではないかなぁと。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-3

包丁クラスがないからおかしいのでは?
調理器具インタフェースを実装した包丁クラスがあれば、シェフは包丁クラスの実装を意識せずに調理器具インタフェース経由で調理メソッドを呼べばいいです。
包丁クラスは調理メソッドで切る以外の振る舞いを持つ必要がないので肥大化しません。
シェフ側もプロキシパターンで実装すれば肥大化しません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-3

chefがcut状態になるわけではないので、chicken.cutです。むしろcutが合わないのでは?
ちなみにchicken.cutは過去分詞、受身のcutですよね。chef.cutの方は現在形。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-3

こういうときは,オブジェクトの方に,書く,ということで,「オブジェクト指向」です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る