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

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

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

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

オブジェクト指向

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

JavaScript

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

TypeScript

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

ドメイン駆動設計

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

Q&A

解決済

2回答

5249閲覧

クラスのインスタンス化時にDIすべきオブジェクトとそうでないオブジェクトの判断の仕方

canvas

総合スコア62

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

オブジェクト指向

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

JavaScript

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

TypeScript

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

ドメイン駆動設計

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

0グッド

3クリップ

投稿2016/08/13 12:26

編集2016/08/14 10:55

#質問
以下に掲載する3パターンのコードのうち、パターン2とパターン3はDI(依存性の注入)と呼ばれるパターンなのだと思います。
逆にパターン1はクラスのコンストラクタ内で依存しているクラスのインスタンスを生成しているものです。

DIすることでオブジェクト間の依存度を下げることが出来るのはなんとなく理解していますが、何でもかんでもDIすれば良いっていう話でもないような気がしています。

というのも、今回のケースではオブジェクトを一つ引数に渡して済む程度の話ですが、依存するオブジェクトが複数あった場合、その数分だけ引数に渡すとなった場合、煩雑な気がしてしまいます。(そこでDIコンテナが登場という話になるのかもしれませんが、それでも、なんでもDIしたほうが良いという話なのでしょうか…?)

質問としましては以下2点となります。

(1)以下のようなコードの場合はどのパターンがより適しているでしょうか?
(2)どういうオブジェクトを、またはどういうシチュエーションではDIをすべきなのでしょうか?

ご教授頂けると大変ありがたいです。よろしくお願いいたします。

パターン1

TypeScript

1class Circle { 2 3 private point: Point; 4 private radius: Length; 5 6 constructor(x: number, y: number, radius: number) { 7 this.point = new Point(new Cordinate(x), new Cordinate(y)); 8 this.radius = new Length(radius); 9 } 10 11 12 get position(): {x: Cordinate, y: Cordinate} { 13 return this.point.cordinates; 14 } 15 16 17} 18 19 20class Point { 21 22 private x: Cordinate; 23 private y: Cordinate; 24 25 constructor(x: Cordinate, y: Cordinate) { 26 this.x = x; 27 this.y = y; 28 } 29 30 get cordinates(): {x: Cordinate, y: Cordinate} { 31 return {x: this.x, y: this.y}; 32 } 33 34} 35 36class Cordinate { 37 38 private cordinate: number; 39 40 constructor(value: number) { 41 this.cordinate = value; 42 } 43 44 get value(): number{ 45 return this.cordinate; 46 } 47 48} 49 50 51 52class Length { 53 54 private length: number; 55 56 constructor(value: number) { 57 this.length = value; 58 } 59 60 get value(): number { 61 return this.length; 62 } 63 64} 65 66 67var circle = new Circle(10, 10, 10);

パターン2

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 get position(): {x: Cordinate, y: Cordinate} { 12 return this.point.cordinates; 13 } 14 15} 16 17 18class Point { 19 20 private x: Cordinate; 21 private y: Cordinate; 22 23 constructor(x: Cordinate, y: Cordinate) { 24 this.x = x; 25 this.y = y; 26 } 27 28 get cordinates(): {x: Cordinate, y: Cordinate} { 29 return {x: this.x, y: this.y}; 30 } 31 32 33 34} 35 36class Cordinate { 37 38 private cordinate: number; 39 40 constructor(value: number) { 41 this.cordinate = value; 42 } 43 44 get value(): number{ 45 return this.cordinate; 46 } 47 48} 49 50 51 52class Length { 53 54 private length: number; 55 56 constructor(value: number) { 57 this.length = value; 58 } 59 60 get value(): number{ 61 return this.length; 62 } 63 64} 65 66 67var circle = new Circle(new Point(new Cordinate(10), new Cordinate(10)), new Length(10));

パターン3

TypeScript

1class Circle { 2 3 private point: Point; 4 private radius: Length; 5 6 constructor(x: Cordinate, y: Cordinate, radius: number) { 7 this.point = new Point(x, y); 8 this.radius = new Length(radius); 9 } 10 11 get position(): {x: Cordinate, y: Cordinate} { 12 return this.point.cordinates; 13 } 14 15 16} 17 18 19class Point { 20 21 private x: Cordinate; 22 private y: Cordinate; 23 24 constructor(x: Cordinate, y: Cordinate) { 25 this.x = x; 26 this.y = y; 27 } 28 29 get cordinates(): {x: Cordinate, y: Cordinate} { 30 return {x: this.x, y: this.y}; 31 } 32 33} 34 35class Cordinate { 36 37 private cordinate: number; 38 39 constructor(value: number) { 40 this.cordinate = value; 41 } 42 43 get value(): number{ 44 return this.cordinate; 45 } 46 47} 48 49 50class Length { 51 52 private length: number; 53 54 constructor(value: number) { 55 this.length = value; 56 } 57 58 get value(): number { 59 return this.length; 60 } 61 62} 63 64 65var circle = new Circle(new Cordinate(10), new Cordinate(10), 10);

追記(14:29)

TypeScript Playground - https://www.typescriptlang.org/play/

TypeScriptのJavaScriptにコンパイルされたコードの掲載リクエストがありましたので追記させて頂きます。

パターン1のJavaScriptコード

JavaScript

1var Circle = (function () { 2 function Circle(x, y, radius) { 3 this.point = new Point(new Cordinate(x), new Cordinate(y)); 4 this.radius = new Length(radius); 5 } 6 Object.defineProperty(Circle.prototype, "position", { 7 get: function () { 8 return this.point.cordinates; 9 }, 10 enumerable: true, 11 configurable: true 12 }); 13 return Circle; 14}()); 15var Point = (function () { 16 function Point(x, y) { 17 this.x = x; 18 this.y = y; 19 } 20 Object.defineProperty(Point.prototype, "cordinates", { 21 get: function () { 22 return { x: this.x, y: this.y }; 23 }, 24 enumerable: true, 25 configurable: true 26 }); 27 return Point; 28}()); 29var Cordinate = (function () { 30 function Cordinate(value) { 31 this.cordinate = value; 32 } 33 Object.defineProperty(Cordinate.prototype, "value", { 34 get: function () { 35 return this.cordinate; 36 }, 37 enumerable: true, 38 configurable: true 39 }); 40 return Cordinate; 41}()); 42var Length = (function () { 43 function Length(value) { 44 this.length = value; 45 } 46 Object.defineProperty(Length.prototype, "value", { 47 get: function () { 48 return this.length; 49 }, 50 enumerable: true, 51 configurable: true 52 }); 53 return Length; 54}()); 55var circle = new Circle(10, 10, 10); 56

パターン2のJavaScriptコード

JavaScript

1var Circle = (function () { 2 function Circle(point, radius) { 3 this.point = point; 4 this.radius = radius; 5 } 6 Object.defineProperty(Circle.prototype, "position", { 7 get: function () { 8 return this.point.cordinates; 9 }, 10 enumerable: true, 11 configurable: true 12 }); 13 return Circle; 14}()); 15var Point = (function () { 16 function Point(x, y) { 17 this.x = x; 18 this.y = y; 19 } 20 Object.defineProperty(Point.prototype, "cordinates", { 21 get: function () { 22 return { x: this.x, y: this.y }; 23 }, 24 enumerable: true, 25 configurable: true 26 }); 27 return Point; 28}()); 29var Cordinate = (function () { 30 function Cordinate(value) { 31 this.cordinate = value; 32 } 33 Object.defineProperty(Cordinate.prototype, "value", { 34 get: function () { 35 return this.cordinate; 36 }, 37 enumerable: true, 38 configurable: true 39 }); 40 return Cordinate; 41}()); 42var Length = (function () { 43 function Length(value) { 44 this.length = value; 45 } 46 Object.defineProperty(Length.prototype, "value", { 47 get: function () { 48 return this.length; 49 }, 50 enumerable: true, 51 configurable: true 52 }); 53 return Length; 54}()); 55var circle = new Circle(new Point(new Cordinate(10), new Cordinate(10)), new Length(10)); 56

パターン3のJavaScriptコード

JavaScript

1var Circle = (function () { 2 function Circle(x, y, radius) { 3 this.point = new Point(x, y); 4 this.radius = new Length(radius); 5 } 6 Object.defineProperty(Circle.prototype, "position", { 7 get: function () { 8 return this.point.cordinates; 9 }, 10 enumerable: true, 11 configurable: true 12 }); 13 return Circle; 14}()); 15var Point = (function () { 16 function Point(x, y) { 17 this.x = x; 18 this.y = y; 19 } 20 Object.defineProperty(Point.prototype, "cordinates", { 21 get: function () { 22 return { x: this.x, y: this.y }; 23 }, 24 enumerable: true, 25 configurable: true 26 }); 27 return Point; 28}()); 29var Cordinate = (function () { 30 function Cordinate(value) { 31 this.cordinate = value; 32 } 33 Object.defineProperty(Cordinate.prototype, "value", { 34 get: function () { 35 return this.cordinate; 36 }, 37 enumerable: true, 38 configurable: true 39 }); 40 return Cordinate; 41}()); 42var Length = (function () { 43 function Length(value) { 44 this.length = value; 45 } 46 Object.defineProperty(Length.prototype, "value", { 47 get: function () { 48 return this.length; 49 }, 50 enumerable: true, 51 configurable: true 52 }); 53 return Length; 54}()); 55var circle = new Circle(new Cordinate(10), new Cordinate(10), 10); 56

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

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

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

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

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

think49

2016/08/14 03:27 編集

TypeScriptをコンパイルしたJavaScriptコードを載せていただけないでしょうか。
canvas

2016/08/14 05:33

リクエストありがとうございます。追記に掲載させていただきました。
canvas

2016/08/14 05:39 編集

ちなみに、上記コードを改良しx座標とy座標をCordinateクラス型にし、半径をLengthクラス型にしてみたのですが、そうしましたら、new Circle()する時の引数がDIしまくるパターンですと、以下のような感じになってしまいました。 new Circle(new Cordinate(10), new Cordinate(10), new Length(10));
canvas

2016/08/14 05:39

コンストラクタの引数はこのように指定します。 //new Circle(x: Cordinate, y: Cordinate, radius: Length)
canvas

2016/08/14 06:07

あ、上記追加コメントに誤りがありました。Cordinateクラスのxと、Cordinateクラスのyをプロパティーに持つPointクラスを作って、それをCircleクラスのpositionプロパティーに初期化時に入れるようにしたのです。それが以下です。
canvas

2016/08/14 06:08

new Circle(new Point(new Cordinate(10), new Cordinate(10)), new Length(10));
canvas

2016/08/14 10:54

コードを全面的に書き換えました。もう1パターン追加いたしました。
guest

回答2

0

ベストアンサー

パターン2とパターン3はDI(依存性の注入)と呼ばれるパターンなのだと思います。

構造的には「コンストラクタ・インジェクション」と呼ばれるDIパターンにみえますね。(ただし、後述する理由によりこれらをDIとは呼べない気がしますが)

(1)以下のようなコードの場合はどのパターンがより適しているでしょうか?

個人的にはパターン1で十分だと思います。というより、質問中のサンプルコード/クラス設計ではDIを導入する意味が無いと考えます。

円(Circle)オブジェクトにとって中心位置や半径という属性は、そのオブジェクトの本質をなす直接的な構成要素です。それらを外部から注入するという設計には違和感があります。また位置(Point,Cordinate)や長さ(Length)を表す型を導入していますが、これらは型安全性(単位系の区別)のために導入されるものであり、DIで用いるインターフェイス継承とは役割が異なっています。

(2)どういうオブジェクトを、またはどういうシチュエーションではDIをすべきなのでしょうか?

関連するソフトウェア・コンポーネント(クラス)にて、あるクラスの内部実装では他方の インターフェイスにのみ依存 するよう実装し、そのインターフェイスを継承/実装した 具象クラスを外部から注入する パターンがDIだと思います。

こういった「インターフェイスと実装の分離」が不可能なクラスにはDIを適用できません(質問中の例が該当)。また、DIによりソフトウェア・コンポーネント実装同士をより疎結合にできますが、過度な疎結合アーキテクチャはメンテナンスコストが増大します。どこまでをDI適用により疎結合にするかは、対象アプリケーションが解決すべき課題次第かと思います。

投稿2016/08/15 02:42

編集2016/08/15 03:01
yohhoy

総合スコア6189

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

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

canvas

2016/08/15 03:27

ご回答ありがとうございます。大変参考になりました。外から注入すべきオブジェクトとそうでないオブジェクトの切り分けの理解が深まりました。ありがとうございます。
guest

0

こんにちは。

回答依頼を頂いたので来てみました。TypeScriptやJavaScriptには詳しくないのですが、「依存性の注入」は言語には依存しない概念と思いますので、分かる範囲で回答してみます。

いきなりですが、バターン1、2、3の相違点が良く分かりませんでした。
単にCircleクラスのコンストラクタのバラメータが微妙に異なるだけのように見えました。C++ではこのような場合、パターン1、2、3のコンストラクタをCircleクラスへ定義します。ですので、依存性の注入云々との関係は読み取れませんでした。

ところで、CordinateとLengthクラスは正直ない方が良いと思います。x座標にLengthを加える等の演算もするでしょうから、わざわざ型を変えて型チェックさせ演算できないようにするのは如何なものかと感じます。
また、事実上何もしていないけど、中身を見るまでは何もしていないことは判らないため、見通しが悪くなっているだけです。

DIすることでオブジェクト間の依存度を下げることが出来るのはなんとなく理解していますが、何でもかんでもDIすれば良いっていう話でもないような気がしています。

DIって要するに抽象化ですね。クラスを抽象化することでそのクラスを使える場面が増えます。
しかし、その分、テスト・ケースが増えるので単体テストはたいへんになりそうです。
折角定数を内部に持っているのにそれをわざわざ外から注入するようにした場合、そのバラメータに様々な値を与えて正常動作することを確認するテストが必要になると思います。
単体テストするプロジェクトであれば、たぶん抽象化は最小限にとどめた方が良いのではないかと思います。

私自身は単体テストに割く暇があるなら機能テストを充実させるべきと考える人なので、プログラムの複雑さをあまり増やさない範囲で実現できる抽象化については実装する方向です。
それなりに複雑さが増えてしまう場合は、抽象化により使用範囲が広がった部分を使う可能性の高さで判断します。使う可能性が低いなら無用にプログラムが複雑化することを回避します。

投稿2016/08/14 13:08

Chironian

総合スコア23272

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

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

canvas

2016/08/15 03:28

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問