オブジェクト指向は難しい

オブジェクト指向で hello.world. を作ることについて考えてみた。
なんというか、hello.world. に過ぎないのにこれが正解っていう答えがない。 オブジェクト指向は「どの方向に拡張性を確保しておくか」が書けるので、そこで思想的なものが回答に影響してくる。

case 1

「"hello.world."という文字を出力する」という機能全体を1つのオブジェクトに詰め込んだコード。
単なるhello.world.を実装しようとするならこれが正解である。YAGNIの法則に従え。
だが、これがオブジェクト指向で書いたhello.world.かと言われると疑問が残る。

class HelloWorldApp
	run: ->
		console.log "hello.world."

case 2

「オブジェクトとは、データと手続きをひとかたまりにしたものである」の概念に従ったコード。
この方式は「単一責務原則」に違反しているのではなかろうかと思う。
これは出力する文字列を変えたい場合と、何に出力するか(画面ではなくプリンタに出すとか)を変えたい場合に、HelloWorldStringを修正する必要が発生する。

class HelloWorldString
	constructor: ->
		@s = "hello.world."
	print: ->
		console.log @s

class HelloWorldApp
	run: ->
		helloWorlString = new HelloWorlString()
helloWorlString.print()

case 3

「オブジェクトとは、責任を実行するものである」の概念に従ったコード。
オブジェクト指向設計的に「正しい」のは、恐らくこれに抽象クラスを加えたものだが、完全に正規化されたDBテーブルが必ずしも良い設計ではないように、プログラム規模によってはそれが良いコードとは言い切れない。

# Hello.World.の文字を作ることの責務を負うクラス
class HelloWorldString
	buildString: ->
		return "hello.world."

# 出力の責務を負うクラス
class Printer
	print: (s) ->
		console.log s

# クラスを組み合わせて希望の動作を実現
class HelloWorld
	constructor: (@printer, @helloWorldString) ->
	print: ->
		@printer.print @helloWorldString.buildString()

class HelloWorldApp
	run: ->
		helloWorld = new HelloWorld(
			new Printer(),
			new HelloWorldString())
		helloWorld.print()