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

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

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

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

オブジェクト指向

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

ドメイン駆動設計

ドメイン駆動設計(Domain-driven design, DDD)とは、ソフトウェアの設計手法、および設計思想や哲学のことです。ドメインモデル構築の際に、設計上の判断を決定する枠組みとドメイン設計に関して議論するボキャブラリを提供するものです。

Q&A

解決済

1回答

2509閲覧

コードレビュー「このコードっておかしいですか?」

canvas

総合スコア62

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

オブジェクト指向

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

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

ドメイン駆動設計

ドメイン駆動設計(Domain-driven design, DDD)とは、ソフトウェアの設計手法、および設計思想や哲学のことです。ドメインモデル構築の際に、設計上の判断を決定する枠組みとドメイン設計に関して議論するボキャブラリを提供するものです。

0グッド

2クリップ

投稿2016/08/14 06:52

編集2016/08/14 11:25

#質問
イメージ説明
書籍「ThoughtWorksアンソロジー」の「第5章 オブジェクト指向エクササイズ」

上記書籍自体はまだ未購入で読んでおりませんが、ネット上でこの本で紹介されている「オブジェクト指向エクササイズ」について触れているページを幾つか見て気になっていたので実践してみました。

実践してみたのは良いのですが、なんだか良くわからなくなってしまいました。
「よくわからなくなった」という部分が質問箇所でして、いかに掲載するコードのコメントに記載しておりますので、そちらのコメントを参照頂けると幸いです。

以下のコードでは「オブジェクト指向エクササイズ」の以下の2つのルールを実践したつもりです。

ルール: インスタンス変数2つ
ルール:プリミティブ禁止

別質問で立てている「円クラスをMVC的に実装したいです!」のコードをリファクタリングする目的で練習がてらに取り組んでみました。

コード

コード内のコメントで「Q:」が先頭についているコメント箇所が自分が特におかしくないか気になっている箇所です。
アドバイス頂けると大変ありがたいです!

TypeScript

1class Circle { 2 3 private point: Point; 4 private radius: Length; 5 6 constructor(point: Point, radius: Length) { 7 this.point = point; 8 this.radius = radius; 9 } 10 11 //左上を起点としたxy座標を返す 12 get position() { 13 return this.point.cordinates; 14 } 15 16 //円の中心を起点としたxy座標を返す 17 get centerPosition() { 18 var positionAtLeftTop = this.point.cordinates; 19 20 //Q.ここは新しいPointクラスのインスタンスを生成して返すべきですか??? 21 return {x: positionAtLeftTop.x.value + this.radius.value, y: positionAtLeftTop.y.value + this.radius.value}; 22 } 23 24} 25 26 27class Point { 28 29 private x: Cordinate; 30 private y: Cordinate; 31 32 constructor(x: Cordinate, y: Cordinate) { 33 this.x = x; 34 this.y = y; 35 } 36 37 get cordinates() { 38 return {x: this.x, y: this.y}; 39 } 40 41 42 43} 44 45class Cordinate { 46 47 private cordinate: number; 48 constructor(value: number) { 49 this.cordinate = value; 50 } 51 52 get value() { 53 return this.cordinate; 54 } 55 56} 57 58 59 60class Length { 61 62 private length: number; 63 constructor(value: number) { 64 this.length = value; 65 } 66 67 get value() { 68 return this.length; 69 } 70 71} 72 73//Q.依存性の注入を愚直にすると、引数がこんなことになってしまうのですが大丈夫でしょうか? 74var circle = new Circle(new Point(new Cordinate(10), new Cordinate(10)), new Length(10));

追記

とりあえず、今回試した2つのルールを実践した結果、小さなクラスが結果的に出来上がりましたが、そのおかけで自然と「座標に関する振る舞いはCordinateクラスでしよう」とか、そういった発想が生まれやすいなという実感はありました。

flied_onionさんへの返答

LengthをCordinateから生成できない気がするんですが、Lengthは2つのCordinateで求められるべきじゃないかな、と。
Cordinateが「二次元の原点座標からの距離」と考えるならLengthもCordinateでいいような気もしてます。

円の半径をLength型で表していますが、Lengthを2点の座標から表すことにすると円の半径をどう表したら良いのでしょう。
円の中心点と外周を結ぶ線が半径ですから、円の半径をLength型で定義するためには円の中心点がまずxy座標ではどこで、そして、外周上の点はx, y座標のどこにするかを決めないと半径を出せないと思うので、なんだか小難しいですね。。。
そうすべきものなのでしょうか?

Lineクラスみたいなものであれば、初期化時に始点、終点を決めて生成するというのは納得です。

Q.ここは新しいPointクラスのインスタンスを生成して返すべきですか???

逆に聞きますが、Pointでないならそれは何を返してるんですか?
それ(設計)次第かと。

メソッドの戻り値を何にして呼び出し元に渡すかは確かにケースバイケースですよね。
単純にxy座標だけ知りたければ、{x: 10, y: 10}みたいな戻り値で十分であるということですよね。

少なくともpositionとcenterPositionは同じ物を返した方が一貫性があっていいと思います。

これは本当ですね! ここは統一すべきだと感じました。ありがとうございます。

私にはこれが依存性の注入に見えませんでした(元がプリミティブだからかな)。
前はCircle内のPointをコンストラクタに渡されたx,yでコンストラクタの中でnewしてたのを、
Pointを受け取るようにして解消したってことでしょうか?

その通りです! 別質問で詳しく投稿しています。
https://teratail.com/questions/44223

今はCordinateクラスもLengthクラスもPointクラスも特にメソッドらしいメソッドがないですが、長さの単位変換をLengthクラスが行ったり、座標変換をPointクラスが行ったりとなるのかなと思っています。

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

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

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

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

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

guest

回答1

0

ベストアンサー

ちょっと書いてみました。
その本は読んでないので回答の質はあまり期待しないでください。(Code CompleteとUncle Bobの本は読んでます)
そういうエクササイズと思って眺めてちょっと気になった点を書いてみます。

LengthをCordinateから生成できない気がするんですが、Lengthは2つのCordinateで求められるべきじゃないかな、と。
Cordinateが「二次元の原点座標からの距離」と考えるならLengthもCordinateでいいような気もしてます。
→ 訂正:「一次元の原点座標からの距離」の間違いでした。


Qに対して。回答というよりは感想に近いです。

Q.ここは新しいPointクラスのインスタンスを生成して返すべきですか???

逆に聞きますが、Pointでないならそれは何を返してるんですか?
それ(設計)次第かと。

Pointでない少し違った意味を持つPositionなるものを用意するなら、違う事もあるでしょう。
ワールド座標・ローカル座標・変換などコンテキストを勝手に解決してくれる機能有するなら変わってくるかなとも妄想しましたが、それでもやっぱり変わらないかなぁ…Pointが今のところ単純な情報しか返さなそうなので。
Pointでない場合、Circleの中心からまたCircleを作りたいとき、その値をそのまま使ってPointになるのか、使い手は不安になるかもしれませんね。
少なくともpositionとcenterPositionは同じ物を返した方が一貫性があっていいと思います。

Q.依存性の注入を愚直にすると

私にはこれが依存性の注入に見えませんでした(元がプリミティブだからかな)。
前はCircle内のPointをコンストラクタに渡されたx,yでコンストラクタの中でnewしてたのを、
Pointを受け取るようにして解消したってことでしょうか?


追記分に関して回答します。

円の半径をLength型で表していますが、Lengthを2点の座標から表すことにすると円の半径をどう表したら良いのでしょう。

すいません、「二次元の原点座標からの距離」と書いてしまいましたが、「一次元」の間違いです。
つまりLengthは直線距離と考えています。
「2点間」という情報を持たせるならベクトルになるでしょう。
で、Cordinateも距離と捉えられ、( x:Cordinate, y:Cordinate はどちらも原点からの直線距離 )

たとえば原点(0,0) 半径 5 の円Aの半径の長さ(Length)と、点(5,0) が中心点の円Bの x座標(Cordinate)
があったとして、どちらも x軸上の原点からの距離と見えます。つまりどちらも同じ物に見えます。

点(0,5)が中心点の円Cがあって、円Aと円Bの中心点の距離(5:Length)と 円Aと円Cの中心点の距離(5:Length)に差を設ける(前述のベクトルの様に)なら良いんですけど、そうでないなら意味のある区分けになっていない気がしました。
図形から離れて、金銭を扱うプログラムでMoneyクラスを設けたとして、今の貯金額(Cordinateに相当)とこれから預金する額(Lengthに相当)を分けて考えている様に見えて不自然だなと感じただけです。
この辺りも設計次第なのでしょうし、強くそうした方が良いというわけでもないです。

単純にxy座標だけ知りたければ、{x: 10, y: 10}みたいな戻り値で十分であるということですよね。

むしろ x,y座標なら、コンストラクタで円の中心をPointで指定させておいて何事かとは感じそうな気はします。座標はPointという契約ではなかったのかと。(Circleのコンストラクタがその形式も受け入れるならそれでもいいです)
toRawCordinates とか、外部ライブラリとの連携のために、リテラル型の情報に返してくれるといったメソッドなら、そういう戻り値でもいいと思います。

その通りです! 別質問で詳しく投稿しています。

そちらは見てませんがDIというと型の交換を可能にしたいなと思うので(個人的にです)、
先の例で言えばLengthが高機能になったり、Vectorが生まれても、
Cordinate, Length, Vector いずれもがMeasurableを実装していてCircleの初期化半径としてはMeasurableを渡せばいいみたいにしたくなりますね。いや、かなり適当に書いてますが。
それによって、CircleはLength, Vectorに依存しなくなるという(現実的にはCircleのなにがしかの戻り値で依存することになりそうですけどね)。

ざっくばらんに書いてみました、これ以上は深く考えるつもりはないです。
駄文失礼しました。

投稿2016/08/14 07:44

編集2016/08/14 12:13
flied_onion

総合スコア2604

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

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

canvas

2016/08/14 11:26

ご回答ありがとうございます!コメント欄ですとタグが機能しないため、質問文にてレスさせて頂きました!
canvas

2016/08/15 03:53

追記ありがとうございます。頂いた回答を参考に再考してみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問