みねこあ

mineko. A! ―from mi-neko online.

2013-09-06

[] オブジェクト指向 と FizzBuzz 20:43  オブジェクト指向 と FizzBuzz - みねこあ を含むブックマーク

OOP らしい FizzBuzz って難しいですね。


side-A

このようなタイトルで記事を書いているのは、当然

さんに影響されてです。「つれづれ」ではなんだか変なテンションで随分と失礼なことを書いてしまったのですが、冷静になれば、やっぱり良い記事ですよね。すみませんでした。

つれづれで書いたとおり「OOPらしさ」については、若干思うところがあります。しかし一般に OOP な開発ではだいたいこのような感じでプログラムが形作られていて、なので、確かにOOPらしいなと思いなおした次第です。

FizzBuzz という簡単なお題を「仕様」として、その実装を通して OOP による開発プロセスをひと通り説明してみせるのは、OO な開発を知らない方に とても良いオーバービューを与えてくれると思います。筋書きも記事の構成も見事で、良い記事だな、と改めて思いました。

ですが、一点とても気になるところがありました。それは、以下の部分。

f:id:minekoa:20130818192830p:image

図1は、現在ある問題がどのように(どのようなオブジェクトで)構成されているのか、そのままをモデルにしたものです。

問題をソフトウェアで解決するには、問題そのものをソフトウェアで表現しなくてはなりません。ここでいきなりデザインパターンのような技法を持ち込むと、モデルを歪めてしまう可能性があります。何らかの指針をベースとしてクラスの表現に置き換えていくのが望ましいでしょう。筆者は『エリック・エヴァンスのドメイン駆動設計』の考え方を元に、次のような指針を用いています。

  • モデル駆動設計を重視する(問題空間のモデルをソフトウェアにそのまま表現すること)
  • 可変性のためのテクニックは、必要が生じた場合に導入する

優先順位としてモデル駆動設計の方を重視しますので、図1のモデルをできるだけそのままの形でソフトウェアに落としこんでいきます。

はてなブログ

この図1 ですが、わたしには 「FizzBuzz の仕様を そのままモデルにしたもの」にはどうしても見えなくって、FizzBuzz の手続き言語での実装から フローチャートのやじるし を省略したものに見えました。省略することで OOっぽく(≒UMLっぽく)みせるだけの、なんといいますか、そう、食品見本のようなものに感じられたのです。


* * *


FizzBuzz 問題が プログラマの間で流行ったのは、以下のエントリーがキッカケだったと認識しています。

どうしてプログラマに・・・プログラムが書けないのか?

この中で FizzBuzz 問題は、

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

どうしてプログラマに・・・プログラムが書けないのか?

のように説明されました。

この問題自体が手続き言語の三大制御構造を満遍なく上手に使えるかを判断するためのミニマム問題という性質を持っています。FizzBuzz問題 の おもしろさは、3と5の公倍数の処理にあります。

後藤さんちの仕様理解では、FizzBuzzを

  • 15で割り切れるケース の置換処理
  • 3で割り切れるケース の置換処理
  • 5で割り切れるケース の置換処理
  • 上記のいずれでも割り切れないケース のそのまま数字化処理

を適用し、かつ、マッチする場合は早い者勝ちでそこ脱出する、という風に解釈しておられました。このモデルでももちろん問題なく動作するのですが、本質的には 3 と 5 の公倍数のとき "FizzBuzz" という合成語を出力するルールは、「15でわり切れる時」という独立ケースと捉えるよりも

  • "Fizz"化 と "Buzz"化 の両方の条件を満たす時、その両方を行う
  • その順序は、除数の大小でソートされている

という法則であると解釈するのが、自然ではないかと考えます。

具体的にいうと、「7でわり切れる時 Pazz と 出力する」というルールが追加になったとき、 3と7の公倍数には「FizzPazz」、5と7の公倍数には「BuzzPazz」、そして 3 と 5 と 7 の公倍数に対して「FizzBuzzPazz」と出力されるに違いない。――という、より抽象的な法則が背後にあるんじゃないかな、という捉え方です。論理合成ですね。

"Fizz"あるいは"Buzz" と "FizzBuzz" の 間の置換文字列の類似が「偶然の一致」でない限り、こちらがより正しいモデルのはずです。*1


* * *


「15で割り切れるケース」を 「3で割り切れるケース」 や 「5で割り切れるケース」と同列・同等に扱うのは、FizzBuzz の広く知られたモデルです。しかしこのモデルは、手続き言語の条件分岐の構造(if-else)の都合に合わせ、より扱いやすい形に元のモデルを変形したものです(注:この変形は DDD でいえば Hands on Modelers 的な ナイスな展開で、良いことです)。

しかし 後藤さんのモデルがこれに引きづられてしまう*2のは、せっかく「OO的」「プログラムの都合に引きづられない 現実そのままモデル」と謳うのであれば、少々もったいない・・違和感を覚える部分です。

――と、重箱の隅を責め立てましたが、どうせなら言い出しっぺが実装してみるべきですね。いうことで、この「より正しく抽象できているであろう」モデルを OO で実装してみたいと思います。PHP はよくわかりませんので、趣味の Python でやりました。

# ルールの構築
class AsFizz(object):
  def isMatch(self, num): return num % self.getDivisor() == 0
  def apply(self, num): 
      if self.isMatch(num): return "Fizz" 
      else: return ""
  def getDivisor(self): return 3

class AsBuzz(object):
  def isMatch(self, num): return num % self.getDivisor() == 0
  def apply(self, num): 
      if self.isMatch(num): return "Buzz" 
      else: return ""
  def getDivisor(self): return 5

replace_rules = sorted([AsFizz(), AsBuzz()],
                        key=lambda rule : rule.getDivisor )

# ルールの適用
for i in range(1,100+1):
    phrase = ''.join(rule.apply(i) for rule in replace_rules)
    print (phrase if len(phrase) != 0 else i) ,
$ ./fizzbuzz.py 
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz

よかった、できて。

apply に失敗したときが空文字のなのは、置換文字列のjoin処理 と どれにもマッチしなかった場合の処理の為の ご都合主義で、美しくないですよねぇ。また、各条件別に別れた処理を合流させる処理(Φ関数)が print と「ながら」になっているのも汚らしい感じがします。にも関わらず ここらへんがシンプルに見えるのは、Python のジェネレーター表記のおかげで、ズルしてます(^^;

よりOO的に美しい構造にするのであれば、一度置換したstring/int 混在リストを作ってから、それを print するのが良いですね。ですが今回は、Python のこのコードの場合、Pythonの文法と「ながら」処理がうまくハマるので、コードが抜群に短くなって見やすい・・というメリットを重視いたしました。

また、class AsFizz と AsBuzz は、「二本立て」をわかりやすくするために別々に書きましたが、まとめて AsPhraseIfDivisible みたいなので表現し、コンストラクタで(3,"Fizz")、 (5,"Buzz") を与えるほうが良いです。同じことを二回書くのはイクナイ、という価値観ですね。Fizz, Buzz に続く第三の要素が出てきたら、迷わずまとめたいな、と感じます。


* * *


今回のモデルは、共に満たす場合のルール(公倍数)を汎化したおかげで、より一段高い抽象が可能になりました。

このモデルのメリットは、「割る数」を増やしていった時の 「複数マッチ」時の組み合わせ爆発や適用順序問題が起きない点です。

たとえば、将来こういう拡張があった場合に、こっちの構造のほうが嬉しいでしょう?

from operator import attrgetter

class AsPhraseIfDivisible(object):
    def __init__(self, divisor, phrase):
        self.divisor ,self.phrase = divisor, phrase
    def isMatch(self, num): return num % self.divisor == 0
    def apply(self, num):
        return self.phrase if self.isMatch(num) else ''

replace_rules = sorted([AsPhraseIfDivisible(3,'Fizz'), 
                        AsPhraseIfDivisible(5,'Buzz'),
                        AsPhraseIfDivisible(2,'Pazz'), 
                        AsPhraseIfDivisible(7,'Dozz')],
                       key= attrgetter('divisor'))

for i in range(1, 300+1):
    phrase = ''.join(rule.apply(i) for rule in replace_rules)
    print (phrase if phrase else i),

これくらいの規模になってくると、ようやく抽象化の効果が、抽象構造を得るためにコードが余分に複雑になった分になんとか見合うようになってきた気がします*3。逆に言えば、これくらいの規模にならない以上は、OOP的にやるよりはシンプルに手続き的に書いてしまったほうが良いコードかな、と思います。

このモデルのデメリットは、「割り切れるときの置換」というものに対して最適化し過ぎている点です。例えば、ソート条件に「割る数」が漏れてしまっているので、このタイプ以外のルールが出てきた時にはよろしくない構造ですね。

ようするに、思惑当たれはムフフですが、外れるとショボンというよくある投機最適化です。この手の仮想のミニチュアルールで 開放閉鎖原則とか考えだすと、「早すぎる最適化」とか「ゴールデンハンマー」にしかならないという典型でございます。


* * *


と、ちょっとモデルとして 個人的に気になった点つついてみました。

わたしは、わたしのモデルのほうが良くFizzBuzz問題を モデル化できていると考えますが、結局のところこのモデリングが「正しいかどうか」は、今後の出現するであろう仕様の拡張次第です。つまり、当たるも八卦ですね。例えば、後藤さんが想定されました「素数という条件の追加」は、わたしのモデルだと厳しいです。

ですので、後藤さんが最後に書かれている通り、

問題をどのようにモデリングするのかには、100%の正解はありません。

また、問題で示した実装も100%正解というわけでもありません。責務の観点でもう少し分割することも考えられるでしょうし、入出力の対称性等に着目した修正も考えられます。

「素数なら」という条件が追加されたら?

「結果をJSONに出力したい」という要望が追加されたら?

要求に合わせてモデルを成長させ、それとシンクロしてソフトウェアも成長させる。

この流れが会得できれば、あなたも立派なオブジェクト指向エンジニアです。

はてなブログ

と言う話かな、と思います。

――〆まで引用でパクってしまいました。


side-B

プログラミングパラダイムは、ほぼ抽象化のパラダイムと言って良いと考えます。そのパラダイムらしい抽象化で、抜群にコンパクトで美しく、読みやすいコードが書ける時の嬉しさは、プログラマの至福です。

さて、side-A では「これがわたしの戦車道OOP道」とばかりに、Python のお粗末なコードをご披露いたしましたが、なーんか、コレジャナイ感が溢れてるのですよね。

OOP と FizzBuzz は食い合せが悪いというか、あんまり「ハマッた」という感じのしない、会心の一撃が難しいというか。

そんなおり、衝撃の FizzBuzz を見つけてしましました。えぇ、もちろん sumim さんですよ。

Integer >> fizz
   ^self fizzBuzz: ((self isDivisibleBy: 3) ifTrue: ['Fizz'] ifFalse: [''])

Integer >> buzz
   ^self fizzBuzz: ((self isDivisibleBy: 5) ifTrue: ['Buzz'] ifFalse: [''])

Integer >> fizzBuzz: fizzBuzzString
   | caller result |
   caller := thisContext sender sender.
   result := ((caller stackPtr > 0 and: [(caller at: 1) isKindOf: String])
      ifTrue: [caller at: 1] ifFalse: ['']), fizzBuzzString.
   caller stackPtr > 0 ifTrue: [caller at: 1 put: result].
   caller stackPtr = 1 ifTrue: [caller push: self].
   ^result ifEmpty: [self]
(1 to: 3*5) collect: [:n | n fizz; buzz]
=>  #(1 2 'Fizz' 4 'Buzz' 'Fizz' 7 8 'Fizz' 'Buzz' 11 'Fizz' 13 14 'FizzBuzz')
Squeak Smalltalk で、カスケードと thisContext を使って FizzBuzz - Smalltalkのtは小文字です

正直、このコードを始めてみた時、あまりの凄まじさに「ここまでやる...」とちょっと呆然としてしまいました。

これほど ポルナレフAA が似合う コードもなかろうかと。――俺は今、Integer に メッセージを 送ったと思ったら それは String だった。しかも送ったそいつはレシーバではなく、一個後ろのオブジェクトがレシーバになってた。sumimさんのコードはいつもそうだね。わけがわからないよ!・・・みたいな。

レシーバにメッセージを送ると、センダーにとって止まった時の中で 自身のコンテキストをいろいろ置き換えられてしまうわけで、まさしくザ・ワールド そのものですにゃあ(しみじみ)。

もちろん、このようなコードは変態で、もし、なんの説明もなくこのコード近辺をデバッグしていたら、ザ・ワールドの秘密を解き明かすことは出来ないと思います。助けて花京院!!


でも、

(1 to: 3*5) collect: [:n | n fizz; buzz]

のコードをみるとわたしは「あぁ、美しいなぁ。 オブジェクト指向的だなぁ。」と思ってしまうのですよ。カスケードメッセージというのが実に美しい。はふぅ。

しかし、このコードの抽象を得るために闇に葬り去られた捨象部分は、なんとも名伏しがたい副作用で、センダーと センダーのコンテキストに冒涜的な操作を働くのです。――あぁ、インスペクタに!インスペクタに!

どうしてこうなる。どうしてこうなった。

*1:実際の分析では、これが本当に偶然の一致だったことによる「偽抽象」を得てしまうことがままあります..orz

*2:とわたしは感じました

*3:数え上げの範囲がさりげなく 1〜300 に広がっている理由は 'PazzFizzBuzzDozz' の出現が 210 だからでございます

2011-06-09

[]「三大」 01:53 「三大」 - みねこあ を含むブックマーク

例の OOP の三大要素「カプセル化(≒情報隠蔽)」「継承」「ポリモーフィズム」は、有名なわりには「詠み人知らず」で、しかもあまり良いモノではありません。

sumim さんによれば、もともとはC++でのOOPを説明する要素から「抽象データ型」「継承」「動的結合」の三つが抜き出され、その後変化した物だと思われるそうです。( オブジェクト指向の概念の発明者は誰ですか?(改訂版) - Smalltalkのtは小文字です)。

そのようは背景から、「三大」は 静的OOPLに偏っており、しかも C++ 界隈ですら、C++ プログラミングスタイルの進化から取り残され、古びて現状に一致しなくなっており、21世紀に入ってからのまともな C++ 本で そのままを使っているのをわたしはみたことがありません。

この「三大」を 「OOPとは何か」の文脈で 取り上げるメリットはもうないのです。(デメリットならたくさんありますが...)


さて、そのような状況ですので、ここは素直に「三大?なにそれ?」と黒歴史化するのが正しい大人の有り様ではあるのですが、そこをあえて、もしこの「三大」の現代版をあなたがでっち上げるとするならば?・・・、というお題に取り組んでみました。

なかなかパズルとして歯ごたえがありますし、自分のOOP の理解度をセキララnしてしまうという意味でも面白いのでないかな?・・と思います。

え、わたし?うーん、そうですね。わたしなら、「(オブジェクトによる)抽象化(≒構造化)」「差分プログラミング」「ポリモーフィズム」になるんじゃないかな、と考えます。


* * *


OOPにおいて抽象化は、オブジェクトというカプセルの中に詳細たるコードを詰め込み、カプセル自身にそれを意味する名前を与えることにより行います。これは構造化プログラミングにおいてプロシージャ(サブルーチン)というカプセルを用いて抽象化するのと全く同じ構図です。

OOPL は ポリモーフィズムを、型継承やダックタイピング(duck typing)、 総称(genericity) で実現し、差分プログラミングを、実装継承や集約(委譲)で実現します。

抽象化は、先の構造化プログラミングの例のように、必ずしもオブジェクトで行う必要はありません。それをオブジェクトで行うモノを OOP と呼ぶだけです。ただし、オブジェクトがクラスのインスタンスである必要はありません。

ポリモーフィズムと差分プログラミングは、それが出来る必要がりますが、どのような方法で実現するかは問いません。 OOP の進化の歴史はこれらに対するアプローチの仕方の試行錯誤でもあるといって良いでしょう。言語とその文化、状況によりどれが可能か/どれが好ましいかは変わるでしょう。

OOP/OOPLとは何か?の文脈で肝心なのは、「何を実現できるか」であり、「どの仕組みでやっているか」とは切り離すことだと思います。


* * *


以下蛇足。

このお題を考えるに当たり、まずは三大から「カプセル化」と「継承」は外すべきだなぁ・・から始めました。というのも「カプセル化」は buzzword 化が著しく(参考:カプセル化、情報隠蔽、データ隠蔽 - ぐるぐる?)、「継承」は 一つの機能で複数の重大な副作用を持つため混乱を招く(加えて、実はOOPL に必須ではない)からです。

カプセル化は「何かを隠蔽すること」と「隠蔽することによりもたらされるもの」のなんでもを指します。たとえば「データ隠蔽」をカプセル化と呼んだり、「実装が隠蔽されることによりポリモーフィズムが導かれる」をカプセル化と呼んだりします。

あまりにいろいろ過ぎて、かえって意味不明になっているのでここは一つに絞りたいところ。問題はなにをチョイスするかです。――うーん、難しい。

けれどもOOPの文脈では「カプセル」はオブジェクトであることは不変ですし、ならばここはシンプルに「オブジェクトによる抽象化」をチョイスするのが「わかりやすい」かな?と思いました。

次に継承。継承を「三大」の中で掲げてしまうことは、

  • 「型継承」と「実装継承」という全く性質が異なるものを一即多に扱わせてしまうことが有害(混乱を生む)
  • (強いて加えるなら)「継承がOOPに必須」とすることでポリモーフィズムを継承の興味深い副作用のように感じさせてしまう危険性がある

と言った点が心配です。とはいえ、典型的な静的OOPL では継承を抜きにOOPL を語るのは骨抜きすぎます。そこで 「継承」そのものではなく「継承が(典型的な静的型付け言語に)もたらすもの」 にシフトさせることにしました。

OOPL界では「手段は異なるが同じようなことが実現出来る。だから俺もOOPLだ!」みたいな名乗り上げるのが伝統ですし、このシフトにより難関であった動的OOPL やプロトタイプベースOOPL にも対応できるものになります。具合がいいかな、と思いました。


さてさて、この「あなたなら『三大』をどう作る?」というお題ですが、きっかけは オブなぜ本2のレビューネタです。

オブなぜ本は OOP を 三大要素――を平澤さんがアレンジしたもの(「カプセル化」を「クラス」に置き換え*1)を軸に説明していて、うーん、なんか微妙に歪な気がするから突っ込もうか・・・でも間違いだとバッサリするのはそれはそれで問題だなぁ、と、あーでもない、こーでもないしてたのですが、じゃあ「まず、わたしならどう書くのだろう、を考えてみよう」をしてみたら、お題として面白かったという次第。

気がつけば、なんだか大喜利のようになってしまいましたが、みなさま如何でしたでしょうか。もし他の方がのっかてきてくれるのなら、なんだか人により答えが様々になりそうで、面白そうであり、恐ろしくもあります。sumim さんのような含蓄の深い人が選ぶ3つは凄そうです。

そしてわたしの座布団は、はたして無事だったのでしょうか、はてさて。

*1:2版で追加された注釈ではさらに「『継承』を『実装の継承』、『ポリモーフィズム』を『インターフェイスの継承』と呼ぶこともある・・みたいな事もちょこっとだけ追加されています。確かに、動的OOPL(「ポリモーフィズム」が「型継承」に依存しない流儀の言語)をハブれば、「抽象データ型」「型継承(含むポリモーフィズム)」「実装継承」で三大を構成するのは良さげです。でもオブなぜ本の本文と合わせるとなぜだか「コレジャナイ感」が漂ってる気がするのですよね

2011-06-08

[]ダックタイピングとポリモーフィズム 01:18 ダックタイピングとポリモーフィズム - みねこあ を含むブックマーク

コンパイル時ダックタイピング - みねこあ にて、過去のわたしは

ダックタイピングは

  • ad-hoc polymorphism
  • subtype polymorphism
  • parametric polymorphism

のどれにあたるか?

コンパイル時ダックタイピング - みねこあ

という点で悩んで、混乱していました。

その点について、英語版のWikipedia Duck typingAd-hoc polymorphism がわかりやすかったので気持ち翻訳してみました。


* * *


まずは、Duck typing - Wikipedia から。

Structural type systems

Duck typing is similar to but distinct from structural typing. Structural typing is a static typing system that determines type compatibility and equivalence by a type’s structure, whereas duck typing is dynamic and determines type compatibility by only that part of a type’s structure that is accessed during run time.

ダックタイピングは structural typing に似ているが、異なったものである。Structual typing は静的型システムで、型のの互換性と同値性を 型の構造から判断する。それに対して ダックタイピングは動的で、型の互換性と同値性を ランタイムにアクセスした部分のみで判断する。

The Objective Caml and Scala languages use structural type systems.

OCaml と Scala が structural type システムを使っている。

Interfaces

Interfaces can provide some of the benefits of duck typing but duck typing is distinct in that no explicit interface is defined. For example, if a third party Java library implements a class you are not allowed to modify, you cannot use an instance of the class in place of an interface you have defined yourself, whereas duck typing would allow this. Again, all of an interface must be satisfied for compatibility.

インターフェイスは幾つかのダックタイピングの利点を提供するが、ダックタイピングは明白なインターフェイスの定義が無いという違いがある。例えば、もしサードパーティ Java ライブラリが あなたが変更できないクラスを実装してた場合、あなたは自分で定義したインターフェイスの代わりに、そのクラスのインスタンスを使うことができないのに対し、ダックタイピングではそれが出来る。また、あるインターフェイスはその全てについて互換性を満たさなければならない。

Templates or generic types

Template, or generic functions or methods apply the duck test in a static typing context; this brings all the advantages and disadvantages of static versus dynamic type checking in general. Duck typing can also be more flexible in that only the methods actually called at run time need to be implemented, while templates require implementation of all methods that cannot be proven unreachable at compile time.

テンプレート、または 総称関数またはメソッドは、ダックテストを 静的型付けコンテキストの中で行う。これは一般に 静的型チェックに対しての動的型チェックのすべての利点と欠点をもたらす。テンプレートはコンパイル時にアンリーチャブルでないと証明されたすべてのメソッドの実装が必要だが、ダックタイピングは実際にメソッドがランタイムに呼ばれる時に実装が必要であるだけ、よりフレキシブルであることができる。(??翻訳に自信なし)ダックタイピングにおいては実行時に実際に呼ばれたメソッドのみが実装されていればよく、一方でテンプレートではコンパイル時にアンリーチャブルであると証明できなかった全てのメソッドを実装する必要があるため、ここでもダックタイピングはよりフレキシブルとなれる。

Examples include the languages C++ and D with templates, which developed from Ada generics.

Duck typing - Wikipedia

次は Ad-hoc polymorphism - Wikipedia。アーリーバインディングの例として overload を紹介したあと、それに対となる レイトバインディングでの Ad-hock polymorphism を紹介しているのですが、なんというか、Smalltalk愛に満ち満ちていて(?)、感動しました。

Late binding

The previous section notwithstanding, there are other ways in which ad-hoc polymorphism can work out. Consider for example the Smalltalk language. In Smalltalk, the overloading is done at run time because the methods (“function implementation”) for each overloaded message (“overloaded function”) are resolved when they are about to be executed. This happens at run time, after the program is compiled. Therefore, polymorphism is given by subtyping polymorphism as in other languages, and it is also extended in functionality by ad-hoc polymorphism at run time.

前項にも関わらず、アドホックポリモーフィズムが働く別の方法もある。Smalltalk言語を例に考えてみる。Smalltalkでは、overload された メッセージ(“overloaded function”) の為のメソッド(”function implementation”)は、execute 時に決定される為、overload は ランタイムで実行される。これらはプログラムがコンパイルされた後の、ランタイムに起こる。それゆえに、ポリモーフィズムは 他の言語のように subtyping polymorphism よりもたらされ、また、ランタイム時の ad-hoc polymorphism によっても機能拡張される。

A closer look will also reveal that Smalltalk provides a slightly different variety of ad-hoc polymorphism. Since Smalltalk has a late bound execution model, and since it provides objects the ability to handle messages which are not understood, it is possible to go ahead and implement functionality using polymorphism without explicitly overloading a particular message. This may not be generally recommended practice for everyday programming, but it can be quite useful when implementing proxies.

しかし、よく見れば明らかなのは、Smalltalkは は アドホックポリモーフィズムのわずかに違うバラエティを提供する。Smalltalkは レイトバインディング実行モデルを持っているので、それがオブジェクトに未知のメッセージをハンドルする能力を提供しており、それが前進して、特定のメッセージの明確なオーバーロードなしにポリモーフィズムを使用して機能を実装すること可能にする。これは日々プログラミングにといて通常のプラクティスとして推奨されないが、プロキシを実装するとき、とても便利だ。

Also, while in general terms common class method and constructor overloading is not considered polymorphism, there are more uniform languages in which classes are regular objects. In Smalltalk, for instance, classes are regular objects. In turn, this means messages sent to classes can be overloaded, and it is also possible to create objects that behave like classes without their classes inheriting from the hierarchy of classes. These are effective techniques which can be used to take advantage of Smalltalk’s powerful reflection capabilities. Similar arrangements are also possible in languages such as Self and Newspeak.

また、一般的な terms における 共通クラスメソッド や コンストラクタのオーバーロードは ポリモーフィズムとは考えられないが、クラスが通常のオブジェクトであるようなもっと均一な言語もある。Smalltalk では、 インスタンスもクラスも 通常のオブジェクトである。In turn 、これが意味するのは クラスへのメッセージ送信はオーバーロード可能で、クラス階層から継承することなしに、クラスのように振舞いでオブジェクト生成を可能にすることが出来る(???)。さらにこの結果、クラスに送られたメッセージもオーバーロードでき、加えてそのクラスがクラスの階層から継承することなくクラスのように振るまうオブジェクトの作成も可能である。これらは Smalltalk のパワフルなreflection 機能を活用するための効果的なテクニックです。よく似た処理 Selfや Newspeak といった言語でも可能である。

Ad hoc polymorphism - Wikipedia

翻訳はかなり怪しいので、ごめんなさい。

結論は、ダックタイピングはダックタイピングということ。dack-typing は structural-subtyping に似ていますが、二人は似て異なる物。

Wikipedia の Ad-hoc polymorphism をみると、強いて言えば実行時 ad-hoc polymorphism といえなくない、すなわち overload の レイトバインディング版と捉えるのが良いのかな?、と思うのですが、これも似て非なるモノっぽいというか。難しいですにゃ〜。


* * *


上記 Wikipedia の記事もわかりやすかったのですが、もっと素晴らしいのは いげ太さんここのエントリーだと思います。

ページが見つかりません:@nifty

必ず読まなければいけないと思います。人として。

あまりに素敵だったので、勢いで「実践 F# 関数型プログラミング入門 」をアマポチしてしまいました。届くのが楽しみです。

るとると 2011/06/09 21:25 翻訳ですが、こんな感じでどうでしょうか。

原文: Duck typing can also be more flexible in that only the methods actually called at run time need to be implemented, while templates require implementation of all methods that cannot be proven unreachable at compile time.
訳案: ダックタイピングにおいては実行時に実際に呼ばれたメソッドのみが実装されていればよく、一方でテンプレートではコンパイル時にアンリーチャブルであると証明できなかった全てのメソッドを実装する必要があるため、ここでもダックタイピングはよりフレキシブルとなれる。

「アンリーチャブルであると証明できなかった」ですね。「アンリーチャブルでないと証明された」だとちょっと意味が変わってしまいます。


原文: In turn, this means messages sent to classes can be overloaded, and it is also possible to create objects that behave like classes without their classes inheriting from the hierarchy of classes.
訳案: さらにこの結果、クラスに送られたメッセージもオーバーロードでき、加えてそのクラスがクラスの階層から継承することなくクラスのように振るまうオブジェクトの作成も可能である。

おそらく“hierarchy of classes”とは、クラスクラスの階層のことだと思います。

minekoaminekoa 2011/06/11 20:39 ありがとうございます。早速本文に反映させていただきました。いつもすみません。


なるほど、「アンリーチャブルであると証明できなかった」になるのですね。確かに「アンリーチャブルであると証明された」では問題です...orz

後者 "the hierarchy of classes" は、継承木そのものを指している・・と理解でよいですね。

2011-03-05

[]MVACってなぁに? 01:03 MVACってなぁに? - みねこあ を含むブックマーク

良いことが書いてあるのでその内容に突っ込む意図ではなく、単に「MVAC」という語についての雑感です。

Web Applicationを綺麗に設計するためのMVACという考え方 - $shibayu36->blog;

わたしのアンテナが低いのか、MVAC というのは初耳でした。のでちょっとググッてみたのですが元ネタがわかりせんでした。本当に「no title」が元ネタなのかな?はてな社内ローカル用語?

というわけで、どこが言いだしっぺなのかよくわかりません。お願い、教えてエロい人。

このお話、個人的にはなんだかとってもデジャブでした。MVC、お前もか - みねこあで感じた「Mってなによ?」的なモヤモヤさが再びです。んー、MVC2 とか MMVC とか MVCS とか MVAC とか いっぱいあると混乱します。それぞれで各要素の絡み方も違えば、Mなどの各要素すら実は違ったりで。

誰かまとめてくれないかしら(sumimさんクォリティで)。


* * *


もうすこしダラダラ蛇足(つまり、デジャブの追体験)をすると、

ここで問題が一つあります。それはControllerが「リクエストをハンドリングする」という役割と、「Modelをいくつか利用して処理を行う」という、二つの違う役割を担っていることです。

Web Applicationを綺麗に設計するためのMVACという考え方 - $shibayu36->blog;

の部分がどうにもイボイボしていて、「Model をいくつか利用して処理をおこなう」も Model じゃん、と思ってしまう。由緒正しき Smalltalk GUI フレームワークに端を発する「MVC」の感覚では、Application といっているのも M じゃないかな。

そもそもの(GUIの) MVCは、単純に、対話型アプリケーションを作る際、変わりやすい Look&Feel *1から、変わりにくい Model を 開放閉鎖原則に従って 責務分割した姿です。

一方、Web の MVC (MVC2)は、構成要素の名前こそ元のMVCをインスパイアして付けられたと思うのですが、その構造はかなり構造が違います。けれどインスパイアされた「構成要素の名前」くらいは、だいたいの意味を引き継いで欲しいと願ってしまいます。

しかし、MVC2 について語られるものを(特にその問題を述べるものについて)読むとき、Controller が Model化してしまうくらいに、 「Model」=「ORMに毛が生えたもの」という定義になってるように感じます。それにより、「MVC」 だけじゃなく、「M」,「V」,「C」 の それぞれもバズワード化しているように思えます。そこまでして「MVC」という語を使う意味があるのかなぁ、とかとか。

「MVC」という語に拘るなら、MVAC という言い回しよりは、gfxさんがはてブコメントで 紹介している

すこし以前から言われていることですが、ORMをMVCモデルにおけるModelそのものとして扱うことで、本来Modelで扱うビジネスロジックをControllerに書いてしまい、Controllerのロジックが増大し、結果的にメンテナンスがしにくい、拡張が難しいコードが生まれてしまいがちです。

(中略)

その対策として、Modelを見直しを行い、ORM、DBI、Web API、他のKVS、そしてmemcachedなどマルチソースでビジネスロジックを記すレイヤーとしての位置づけとすることを考えてみたい。多くの情報を扱う今、こちらの考え方が自然に設計ができると考えます。

Re: @kazuho: handlersocket plugin や mycached を使えば memcached は不要か、それとも使うべきケースがあるか。考察せよ [10点] - blog.nomadscafe.jp

(強調はわたし)


のほうが、語の使い方として個人的にしっくりきます。


* * *


ここらへんはの用語の混乱のお話は、以前のデジャブエントリで頂いたコメント

umejava

2009/10/19 08:31

WebのMVCですが、Domain-Driven Designの言葉で語れば混乱は生じないと思うんですよね。http://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap1.html

みねこあ

のように、MVC じゃない語の方が筋がよいと思います。みんな DDD 用語で語ろうよ!名前重要だよ!・・ということでどうでしょう?

タイムリーなことに DDDはもうすぐ日本語で発売ですよ!

みんな、買おうぜっ!

*1:Look が View で、Feel が Controllerです

2010-11-27

[]憂鬱本は死なぬよ。何度でもよみがえる 23:19 憂鬱本は死なぬよ。何度でもよみがえる - みねこあ を含むブックマーク

なんか定期的に JavaBlack さんからわたしの憂鬱本のエントリにトラックバックが飛んできます。本当に「憂鬱本は死なぬよ、何度でもよみがえる...」なのかしら。

というわけで、JavaBlack さんのエントリの元のエントリを拝見させて頂きました。

今回はいつもと違って不思議な光景が浮かんでいて新鮮でした。だって、憂鬱本と Head First デザインパターンが軒を並べているのです。

憂鬱なプログラマのためのオブジェクト指向開発講座―C++による実践的ソフトウェア構築入門 (DDJ Selection)

C++ によるオブジェクト指向への本。

シューティングゲーム等を例に挙げて、その処理の責務を持つのはどっちのクラス?がわかりやすく書かれてます。

超良書、有名だよね。

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

本屋で必ず目に入る本(笑)

パラパラめくった事ある人は写真だらけで、読まず嫌いしてる人もいると思います。

が、絶対読むべき。

デザパタ丸暗記じゃなくて、各パターンがどんな感じの仕組みになってるか、すげーわかりやすいです。

ERROR: The request could not be satisfied

方や ポリモーフィズムやhas-a を「大したことない」的に否定しちゃう本、かたやポリモーフィズムや has-a を重視して分析設計しちゃう本。・・・この二冊って水と油っぽいと思えるのだけれど、実際にプログラミングするとき「で、結局どっちなのさ」と混乱してしまわないかしら。


* * *


憂鬱本はOOを、概念を現実模写的に階層構造化して分類すること(=「モデリング」)と定義しているのですが、このアプローチって プログラムの設計としてよいものになるとは限らない・・最悪プログラムにそのまま落とせなかったりすることが多い「モデリング」だったりするのですよね。90年代後半のOOブームで(C++界隈で)コレをやってみて、結局「この生き方はダメですっ!(byちよちゃん」と実感した人は大変多いと思います。

このような「ダメですっ...orz」を痛感したプログラマにとって魅惑なのが、GoFのデザインパターン。その序文

一度デザインパターンを理解し、「ん?」ではなく「あっ、そうか!」という経験をすれば、今までと同じ方法でオブジェクト指向設計をしようとは思わなくなるだろう。そして設計プロダクトの柔軟性、モジュール性、再利用性、および理解のしやすさをより高めるためにはどうすればよいかがわかるだろう。そしてまさしくこれらのことこそ、読者の皆さんがそもそもオブジェクト指向技術に興味を持った理由ではないだろうか

にも、「これ、これですよ!私たちが求めていた OOはっ!」とウンウンうなずいたモノでした。

また憂鬱本は、OO に対して「現実を模すもの」と拘りまくっている故に大変おかしなことになっているところもあって、具体的には、オブジェクトをコピーすること、クラスの属性にクラスを持つこと、一機能をオブジェクトに切りだして委譲すること、「状態」ではなく「振る舞い」を主体として抽象化したクラスやオブジェクトを作ること、を「現実にはそんなモノないでしょ」と切って捨てていて、それゆえ多態性を不当に軽視しています。

というわけで、今のご時世で憂鬱本に習うことは、結局 デザインパターン的な設計に まったく繋がらない(=プログラムの設計と分断された)分析モデルを作ることにしか成らないと思います。

憂鬱本自体がその分断を肯定していることが、また今となってはイけてないのですけどね*1。(今時は Domain-Driven Design 的に、モデルとコードの相互フィードバックを得られることが OO のメリットの一つと考えられてたり)

わたしは後知恵で「当時としてもおかしいでしょ?」と思うのですけど*2、当時としてはあれが良い本だった・・という意見も結構見ます。それは棚上げしても、今となっては良くない本だというのは、動かないかな、と思います。


* * *


初心者の感じる「わかりやすい」って、実のところ単に「読みやすい」でしかなかったりします*3。だから「読みやすい」は「役に立つ情報」と関係ないし、初心者が読んだ印象だけではそれが良書か悪書かを判断できません。

憂鬱本が多くの技術者に「有害」と言われつつも、いまだに高い評価をする人が絶えないのは「わかりやすい」本だから。

さらに厄介なのは、正しくなさをどんなに誰かが主張しても、初心者にはそれが正しいか否かを、主張している人が信じられそうか、とか、多くの人が言っている、とか、そんなことでしか判断できません。

Head First デザインパターンをおすすめだとわたしが考えるのは、本当に右も左もわからない「OO初心者」が「わかりやすい」と思ってくれることだと思ってます。「解りやすさ」じゃ憂鬱本に負けてないのが Good!

そう言う意味で、この本が憂鬱本と「並び立った」のはうれしいことかな?と思ったり。

*1:上手く実装に落とせないという実践上の実感と、そこは割り切るという結論は論理的には正しい

*2:Smalltalkへの言及があるのですが、絶対ちゃんとさわってないでしょ!・・という内容なのです。なのに 「言語を問わず OOP 全般にこの本は当てはまるよ」と言い切ってるのをみると、なんだかなー、と思わざるを得ない/多分Simulaもちゃんと弄ってないと思う

*3http://d.hatena.ne.jp/kilrey/20090426

interleaveinterleave 2010/11/30 01:50 C++限定で英語でよければ、やっぱり http://www2.research.att.com/~bs/C++.html ですかね。説明もコードもあって、2つ目以降のプログラム言語としてちょっとC++に手を出すには手軽でよいかと。C++自体が手軽な言語かはまたちょっとアレですが。
それはそれとして"C++ is a general purpose programming language with a bias towards systems programming that "という一文を見る度に、世の中におけるC++の認識が(以下略)

JavaBlackJavaBlack 2010/12/04 16:08 >床が抜けそうですし、
そこでAmazon Kindleですよ.

>万年貧乏だったり。高いですよね技術書って。
後者の問題については、なあに帰って免疫が...悪化します。orz
土地問題がなくなると歯止めが効かなくなりますね....