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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Java

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

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

オブジェクト指向

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

11回答

16881閲覧

テストのためにメインのコードが見づらくなるのは皆諦めてるもの?

lazex

総合スコア604

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Java

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

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

オブジェクト指向

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

12クリップ

投稿2017/05/19 15:09

編集2017/05/24 12:57

一般的なオブジェクト指向言語全体に言えることに言えることで、言語ごとにこういうのあるとか聞けると良いかなと思ったのでタグは色々つけています


ひとつめですが、 static method にするか、 instance method にするかについです

個人的には instance method は this のプロパティや他の instance method を参照するもののみで、インスタンスが持つデータへのアクセスが不要で引数だけから返り値が決まるもの(シンプルなものだと 2つの引数を足す add など)は static にしたいです
プロパティを参照しないものは static に分かれていたほうが見やすく、知らないクラスを見たときにも、このメソッドはプロパティ参照しないことがわかるのはいいことだと思います

しかし、知人から聞いたところテストを考えるとプロパティの参照がなくても instance method にした方がいいとのことでした
その理由は、 static にするとそのメソッドが呼ばれることが決まってしまうが、instance method であればインスタンスをモックに置き換えることで、テストに都合が良いようにできるというものです

add みたいなシンプルなものは不要ですが、返り値のデータが大きく(複雑な構造のオブジェクト)なりがちでそれを使う側のテストではシンプルなデータのほうが確認し易い場合、単に処理に時間がかかる場合、細かい仕様が決まってなく中身が未完成だけど使う側はテストしておきたいとき、などを考えると一理あるように思えます

しかし、テストは作るアプリケーション単体には必要なものでなく、作る側が楽に確認するためのものであり、テストだけのためにほぼ全てのメソッドを static method から instance method にするようなプログラムを見づらくすることは避けたいです

テストのしやすさをとるか、アプリのプログラム単体を見てわかりやすくなるようにするかどちらかをとるしかないものでしょうか?
みんな仕方なく、static でいいものも instance method にしているのでしょうか


2点目です
こっちはC#固有のものかと思います
上で書いたようなモックにインスタンスを置き換える場合です

AクラスがBクラスのインスタンスをもつときに、BをBを継承したB2クラスに置き換えます
Bクラスで各メソッドが virtual 指定されていなければ、override でなく new になり、Aクラス内のBがB2であろうと、Bクラスとしてメソッドが呼ばれるのでB2クラスでなくBクラスのメソッドが呼び出されます

ほとんどのクラスはどこかのクラス内で保持される思うので、テストを考えるとほぼ全部のメソッドがテスト以外に意味もなく virtual になります

有名らしいMoqというライブラリも virtual つけないといけないそうですし、これについては他に方法はなさそうです

テストのためだけに全部に virtual つけていくのが良い書き方とはとうてい思えないのですが、みんな諦めて書いてるものなのでしょうか

謝辞

思った以上にたくさんの回答いただき驚きました
オブジェクト指向って難しいですね
いつまでもオープンのままにしておくのもどうかと思ったのでこのあたりで終了とします
ベストアンサーはすごく迷ったので、一番高評価が多かった回答にしました

ありがとうございました

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

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

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

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

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

guest

回答11

0

ベストアンサー

「ひとつめ」

テストのためにインスタンスメソッド化するのではないのです。
疎結合にするためにインスタンスメソッド化し、疎結合化した結果としてテストもしやすくなるものなのです。

各クラスがスタティックメソッド群に依存しきってしまっていたら、クラスそれぞれを取り出して部品として扱うことは適わなくなります。それを

  • スタティックメソッド群をインスタンスメソッド化し
  • そのクラスはそれ用のインターフェースを実装した形にし
  • 利用する側のクラスはインターフェースへの参照だけ持ってそれに対してアクセスする

という形にすることでクラスからメソッドへの結合を引きはがせる、という話です。これによりメンテナンス性、分業しやすさ、クラスの性質の把握しやすさが向上します。テストもしやすくなっています。

「ふたつめ」

AがB型の参照を持つのではいけません。BのインターフェースをIBへ抜き出し、B implements IBの形にします。AはIB型への参照を持ってアクセスします。そしてテスト用のモックはIBを実装する形で作るのです。

投稿2017/05/19 15:50

yuba

総合スコア5568

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

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

lazex

2017/05/20 14:39

疎結合はよく耳にはしますが、テスト以外にメリットを感じたことがないので、私の中ではテストのために疎結合にする、になってるんですよね・・・ +が多いので疎結合が流行りというかメリットあると感じるひとは多いのでしょうが、staticであるメリットを捨てるのと天秤に書けて疎結合を取ろうとは思えないのが現状です ふたつめですが、全部にvirtual書くのともう一つの方法としてインタフェースを使うというのは把握してます ただ、これもやってみたのですが、インタフェースを切り出す必要性を感じないクラスもテストのためにインタフェースを作って、ほとんどのクラスにそのインタフェースもできてしまい、クラスの構造がかわるときにインタフェースの方も修正が必要で手間がかかるようになり「テストのためにアプリが複雑化する」を強く感じたんですよね
koko_u

2017/05/21 00:59

> staticであるメリットを捨てるのと天秤に書けて疎結合を取ろうとは思えないのが現状です この辺に大きなギャップを感じますね。 質問者さんの言う、static であるメリットについて、質問文の内容で共感を得ることは非常に難しいと感じます。
yuba

2017/05/21 09:54

staticであることのメリットというのはありますから、疎結合であることを絶対視しろとは言いませんです。 疎結合だと例えばコード全体を読み解くときに飛び先がわからないので処理を追えませんし、書きたいことをダイレクトに書き下ろせないことが思考のテンポを邪魔してきます。 とはいえ、疎結合のメリットはテストだけではありません。まず第一には再利用性です。前にやったプロダクトと同じようなコードを同じようにまた書いていることがないか。 それをライブラリ化してチーム内で、もしくは全世界で共有しようにも普通はそう簡単にはできないのです。プロダクトコードからそう簡単に「ある処理」のコードだけ引きはがせないので。 ところがそれをできるようにしよう、という設計指針が疎結合であると考えてください。 テストがしやすくなるのは結果です。単独で取り出しやすいコードなら、単独でテストするのもそりゃ簡単ですよという。 ですので、絶対視も軽視もせず、要件と必要性をみてバランス取っていこうっていうごくつまらない結論となります。
lazex

2017/05/21 13:08

> 飛び先がわからないので処理を追えません これは私も感じたことあります 疎結合に積極的にしようと感じられない1つでもあります > まず第一には再利用性です。 なるほどです メリットについてはわかりました > 絶対視も軽視もせず、要件と必要性をみてバランス取っていこう どこで疎結合をとるか、とらないかなかなか難しいですね そこで「テストを理由」に疎結合をとるかを考えると当初の疑問に戻るので、 とりあえずアプリケーションのコードだけをみて、「再利用する可能性があるか」を基準に考えてみようかと思います もしよければyubaさんの場合はどういうものは疎結合にする・していないかの具体例を教えてもらえるとうれしいです
yuba

2017/05/21 13:25

私も偏っていてあんまり参考にならないと思います。 というのは、疎結合を重視するのはJavaの価値観というところがあり、見た目は似た言語のC#の世界ではそこまでうるさく言われないものなんですけど、私はC#書くときにもまず「引きはがしポイント」から設計を考え始めてしまうくらいには疎結合脳なので。 だからこれは具体例というよりは私の頭のおかしさの実例と言うべきなのですが、 http://qiita.com/yuba/items/f52f90c4bd249d24b7d6
lazex

2017/05/23 13:09

具体例ありがとうございます 参考になりました
guest

0

こんにちは。

経験的に「不必要な依存はしない方がよい」という原則は結構頑張って守った方が信頼性の高いプログラムを開発できると思います。
例えば、なるべくprivateにするとか、グローバルにアクセスできる変数はなるべく減らすなどです。
この原則に則ると、instance変数に依存していないstatic methodを、instance methodに変更して依存させるのは、ナンセンスと思います。
逆にいうと、テストしやすいからprivateにしないでpublicにしましょうと言っているのと同じように聞こえてしまいますね。

一部のメソッドだけを本番コードに影響しないように置き換えるって結構難しいですが、そもそも本番コードの設計レベルに影響するような置き換え方法は「無し」と思います。従来通りの泥臭い方法で置き換えるしかないのではないでしょうか?


【追記】
ちょっと質問の主旨から外れますが、instance methodの方が良いと言っている人も少なくないことにちょっとびっくりして、考えてみました。

オブジェクト指向プログラミングって非常に強力です。一般に手続き型フログラミングより適切である場面が多いです。(直感的には95%くらい? もしかすると99%くらいかも。)

そして、instance methodはオブジェクト指向プログラミングの代表的な仕組みの一つであり、static methodは昔からある手続き型プログラミングの代表格です。
なので、一般的には手続き型よりオブジェクト指向型の方がよいよと言う意味で、instance methodを推奨している人も居るような気がします。

一般論では全くその通りと思います。もしも、static methodが異様に目立つプログラムを見たら、オブジェクト指向を使いこなせないプログラマーが作ったのかな?とか思うかも。
しかし、ご質問のケースはstatic methodの方が設計的に適切な場合の話と感じたので、上記の議論には特に触れませんでした。

オブジェクト指向プログラミングが適切なケースは非常に多いので、結果としてinstance methodを使った方が適切な場面は多いと思います。しかし、手続き型プログラミングが適切な場合が皆無というわけではないのでその時にstatic methodを使った方が良い場面も少しはあります。後者を否定するとなかなか悲惨なプログラムが出来上がります。

投稿2017/05/19 15:43

編集2017/05/24 04:53
Chironian

総合スコア23272

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

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

lazex

2017/05/20 14:32

> 不必要な依存はしない方がよい > なるべくprivateにするとか、グローバルにアクセスできる変数はなるべく減らすなど > テストしやすいからprivateにしないでpublicにしましょうと言っているのと同じように聞こえてしまいます これは私もそう思います なのでテストのために変えるのは気が進まないんですよね 後半はモックを置き換えではなくテストコードの方でがんばるという意味であっていますでしょうか
Chironian

2017/05/20 15:09

モックが必要な時はモックを作って本番のコードと置き換えるしかないと思いますよ。それが例えば#ifdef(C++erなんで)を使うような泥臭い方法でもという意味です。 もちろんなるべく泥臭い方法は避けますが、そのために本質的に不必要な依存を導入した結果、設計的にはアクセスするべきでない変数にアクセスするコードをいつの間にか誰かが書いてしまい、後でにっちもさっちもいかなくなることを恐れます。
lazex

2017/05/21 12:33

そういう意味でしたか テストのためにifを作る、というのは出来る限り避けたいものですが、intance method化やpublic化などをしたことで別の問題がでてしまう可能性を考えるとしかたないですよね
Chironian

2017/05/21 13:08

そういうことですね。 テストのためのif文は確かに嫌なものですから、static methodのままですげ替える良い方法が見つかるとよいですね。
lazex

2017/05/24 12:52

追記拝見しました 場合に応じオブジェクト指向と手続き型の適切な方を選択するようにしたいと思います ありがとうございました
guest

0

C# は詳しくないので、ぼんやりした回答になってしまいますが。

static method にするか、 instance method にするか

一般的に、何らかの状態を管理する目的でクラスは作成されます。
管理している「状態」に関係するメソッドがinstance method で クラス自身の何か、例えば生成の Factory method がほしい場合に static method となるので、悩む事はあまりありません。
悩む場合、そもそもメソッドを定義するクラスが妥当かを考える方が良いでしょう。

知らないクラスを見たときにも、このメソッドはプロパティ参照しないことがわかるのはいいことだと思います

利用する側が、プロパティを参照している / していないを意識する必要がある場合、そのクラスのカプセル化が十分でないことを示しており、クラスの責務や提供する機能が何かを再考するとよいです。

instance method であればインスタンスをモックに置き換えることで、テストに都合が良いようにできるというものです

モックのやり方は色々あり、特定のテスティングライブラリの機能にこだわる必要はありません。

テストで他のクラスとの依存関係をモックに差し替えたいと考えたなら、実際のアプリケーションとしてもその部分を疎結合にしたほうがよいことを示しています。
テスタビリティが上がるようにコードを書いたつもりで、読みにくいコードを書いてしまうことはあります。
しかしそれは単に読みにくいコードを書いてしまっただけで、普通に書き直せばよく、どちらかを諦めるという話ではありません。

投稿2017/05/19 23:48

koko_u

総合スコア936

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

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

lazex

2017/05/20 14:56

> 一般的に、何らかの状態を管理する目的でクラスは作成されます。 そのクラスのインスタンスを作るファクトリメソッドと状態を変更するメソッドのみなら問題ないように思えます 私がstaticにしたいと書いたものはそのクラスでしかつかわれないような固有の処理の「関数」で、状態問わず引数から返り値を状態によらず変換するものなんです > 利用する側が、プロパティを参照している / していないを意識する必要がある場合 意図としては利用する側の立場ではなく、他人が書いたコードを読む場合や自分が昔書いたコードを忘れた頃に読んで、そのクラスの全体象を把握したいときです > モックのやり方は色々あり、特定のテスティングライブラリの機能にこだわる必要はありません。 なるほど 他の回答者さんの回答をみるとJavaやPythonはこういう問題も対処できるようですし、これがオブジェクト指向におけるテストの唯一の方法ではないですね
koko_u

2017/05/21 00:49

いかんせん、具体的なコードがないので話がかみ合っていないことを承知で > そのクラスでしかつかわれないような固有の処理の「関数」で、状態問わず引数から返り値を状態によらず変換するもの その「関数」が非常に単純なものであれば、そもそもテストを書く必要はないです。 private static なメソッドとして好きにすればよいでしょう。 テストを書かなければ不安になる程度に長いコードが、クラスの状態にまったく関係なく、static なメソッドとしてクラスに実装されている場合、 クラスの設計に問題があるケースがほとんどです。その長いコードの責務を受け持つべきクラスを抽出することを検討しましょう。 > 他人が書いたコードを読む場合や自分が昔書いたコードを忘れた頃に読んで、そのクラスの全体象を把握したいとき 確かに、static メソッドになっていれば、そのクラスのフィールドを変更しないことが読み手にわかりますが、動機付けとしては弱いです。 個人的には「メソッドの置き場所を考えるのが面倒なので、このクラスに突っ込んでおけばいいや」的な印象を受けます。
lazex

2017/05/21 12:50

>具体的なコードがないので こちらわかりづらくすみません 特定言語というよりオブジェクト指向の考え方みたいな意味で質問したかったので特定言語での例は控えました >動機付けとしては弱いです > static であるメリットについて、質問文の内容で共感を得ることは非常に難しい staticというか状態を考慮せず副作用起こさないことが保証されていることは、「変更するつもりのないものをgetterのみやconstにする」などと同じで読みやすさで重要に思うので、状態に触れないかつそのクラスに関わる処理はstaticにするのは私としてはできるだけそうしておきたいものです ですが、そう感じず困らない人もいるわけですし、コードの読み方について見直すという考え方もありかもですね
guest

0

僕は一応C#でごはん食べてます。

回答1.static メソッドをわざわざ instance メソッドにすることはありません。
あるクラスに対して固有なものであっても、staticメソッドは混在しないようにします。
他の方の回答へのレスでファクトリメソッドを例に挙げていますが、ファクトリメソッドはファクトリクラスに纏めて外部化すべき部分です。
そのようにしていくと、最終的にstaticメソッドとinstanceメソッドの共存は無くなるはずです。

回答2.virtualが必要なMockライブラリを使いません。
Moq以外のライブラリを使えば防げる、という他の回答にあるように、モック生成という目的に手段の実装レベルが追いついていないがために起きた不幸です。
まだまだな部分もあるかもしれませんが、今ならinterfaceさえ用意すれば簡単にモックを作れます。例えばNSubstituteだったりFakeItEasyを調べてみるといいです。

質問がでっかすぎて小さなレスポンスが僕の脳みそだと書けません。
こういうの熱く長文書くと間違ってたら恥ずかしいから嫌だなぁって思いますが…
コードこみこみ100行超過して回答をレスしていいならコメントくれれば追記します。
とりあえず、実装したメソッドのテストカバレッジ100%を目指すとか、〇〇を使うが社内ルールだとどうしようもないんですが、そうじゃない場合、ユニットテストのためにコードを捻じ曲げる前に正しい方法が見つかると思います。

着目してることは正しいと思います。問題はたぶん、クラスの粒度が荒すぎるんです。

投稿2017/05/22 13:26

編集2017/05/23 09:09
haru666

総合スコア1591

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

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

lazex

2017/05/23 13:03

そのクラスに関係する部分でしか使わない処理であろうと違うクラスにしてしまうということもできますね ですが、 > static メソッドをわざわざ instance メソッドにすることはありません とあるので static メソッドのみのクラスは static メソッドのままかと思います その場合やはりこれらのメソッドを使うメソッドをテストするときにモックに置き換えができないのではないですか? C# でモック使う例ではほとんどMoqの例で、昔soで質問したときもMoqならこういうことが~と回答もらったのでC#のスタンダードなものかと思っていましたがそうでもないのでしょうか テストに何をつかわないといけないとかはないので、教えていただいたものを調べてみます
haru666

2017/05/24 01:25 編集

僕にはstaticメソッドをモックにして単体テストするっていうのがよく分かりません… staticメソッドは直接的に依存することを目的とした参照透過性のある不変の関数です。 先にユニットテストを書き、単体でテストされている状態を必ず作ります。 staticクラス/メソッドには依存性が無いため確実にテストできます。 しかし、不具合や仕様変更でこういったメソッドも中身は変更されます。 staticメソッドに依存しているクラスは一緒にテストされるべきだと考えています。 そのため、モックと差し替えたいという考えになりません。 モックに差し替えてしまうと交換性の無い詳らかにすべき依存性もテストされなくなってしまいます。
haru666

2017/05/24 02:22 編集

そういえば他のコメントで、オブジェクトの内容を参照しないからstaticっていう話がありましたが、他オブジェクトと交流する時点で私はこの考えには否定的です。 interfaceを作成すると明らかになることですが、interfaceを継承したオブジェクトには内部状態を見ないメソッドがしばしばあります。例えば、Is〇〇という状態判定メソッド、例えばIsReadOnlyは常にtrueだったりfalseを返しますが、このメソッドがstaticにならないのはなんとなく感覚で分かりますよね。 これは「今たまたま内部状態に影響を与えない/受けない」メソッドになっているだけ、というような考え方ができます。 基本的にオブジェクトの中身の設計に依存せず、内部状態を参照する可能性が将来にわたってある、と考えることができます。このようなメソッドの場合、staticメソッドにするのは当然誤りになりますよね。単一種のクラス設計になっていると勘違いしがちですが、何かのオブジェクトに依頼することが形式として自然であれば、それは内部状態の参照の有無に関わらずinstanceメソッドであるべきです。 そうしなければ変更に対して開いておくことができないからです。 interface経由の処理を実装していれば、自然とアップキャストすることになるのでこの点に対する感性は変わってくると思います。
lazex

2017/05/24 12:30

> 内部状態を参照する可能性が将来にわたってある なるほど IsReadOnlyだとそのオブジェクトの readonly プロパティを参照するのでthisを使いますが、Array.IsArrayみたいな引数を純粋に判定するメソッドとか、どうやっても状態を見る必要が思い浮かばないのもありますよね 今のところすっきりはしないのですが、こういうのでも「状態を参照する可能性は0じゃないんだ」と考えてしまうというのがオブジェクト指向なんですかね・・・ なかなか難しいです
haru666

2017/05/25 03:00 編集

オブジェクト指向だからそうする、っていうより、オブジェクト指向だと「そうなる」というべきです。 あるListをWrapして内部状態を変更する操作全てを禁止し、読み取り専用リストを作ることを考えてみてください。このような変更を加える場合、ReadOnlyプロパティは内部にWrapしたListのReadOnlyに関わらずtrueでよくなります。あえて「リストの状態を変更する操作を禁止する」ことでReadOnlyに変更するわけですから、そもそも元のCollectionはReadOnlyプロパティがfalseなはずです。 ※実際にこれを行うReadOnlyCollectionというクラスがあります ListのReadOnlyプロパティは元々そういうシナリオが存在することを前提に追加されているので、内部ListにDependantな実装ではIsReadOnlyは内部のリストの値を外部に返し、内部Listを拡張する場合(読み取り専用に変化させる)ではReadOnlyは参照しなくなります。 Array.IsArrayはC#のメソッドにはありません。IsArrayはTypeに紐づくインスタンスメソッドです。 String.IsNullOrEmptyメソッドが外部にあるのは、その方が低コストで済むということの他に、オブジェクトがnullであることを調べる方法はstaticメソッドにするしかないからです。 また、stringオブジェクトは組み込み型で代替の必要ない実装であることから強依存できます。 stringオブジェクトは直接staticメソッドを持つ条件を満たしています。 オブジェクトの実装を切り替える可能性があるところではinterfaceと、そのinterface用のstaticメソッドという組み合わせになり、interfaceの実装クラスは切り離され直接的に依存しなくなるべきです。 実装クラス側に後の処理で使われるpublic staticメソッドを持つのはNGです。それをすると別の実装を作っても、元の実装クラスの参照を切ることができなくなります。 これらの条件を全て繋ぎ合わせると、mock化される対象となるクラスは必然的にpublicなstaticメソッドを持つことはありえなくなります。
guest

0

プロパティを参照しないものは static に分かれていたほうが見やすく、知らないクラスを見たときにも、このメソッドはプロパティ参照しないことがわかるのはいいことだと思います

そのメソッドは本当にそのクラスメンバとして実装するのが妥当でしょうか?

インスタンス内部の状態に全く依存しないということは、演算子や書式化など、本来のクラスの責務とは少し異なる目的を持ったメソッドである可能性が高いんじゃないでしょうか?

そういったメソッド、例えば XXX クラスのファクトリメソッドであれば XXXFactory クラスに追い出して、本来のクラスの責務とは別物としてテストを書くことが出来ます。

また、C# であれば拡張メソッドとして記述することで、やはりクラス本体から切り離して記述することが出来ます。

投稿2017/05/20 02:56

編集2017/05/20 02:57
hidori

総合スコア402

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

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

lazex

2017/05/20 15:08

> そのメソッドは本当にそのクラスメンバとして実装するのが妥当でしょうか? ちょっとした演算などでもそのクラスでしか使わないだろう独特な演算ならばそのクラスのメソッドとしたいです そのクラス以外でも使う可能性がある汎用のメソッドならUtilityなどの名前でstatic class を作ってそこにまとめるようにしてます ただこの Utility にしたところで、Utility.Xxxx というメソッドをモックに置き換えてテストしたいとなれば Utility は static でなくそれぞれを instance method にしないといけないので、問題は変わっていないと思います
guest

0

個人的な指針ですが

1つ目

私の場合、基本的にinstanceクラス設計します。
拡張メソッドを作るときにはstaticクラスとしますが、それ以外はinstanceクラスとして設計します。

オブジェクト指向として考えた場合、
・クラスの継承
・インターフェイスの実装
は機能を交換可能なものとして実装するための設計です。
そう考えたときに、staticクラスでは、交換可能な実装になりません。
今、交換が不要でも、将来交換が必要になるかもしれない場合、
instanceクラスとして設計しておくことがメリットになると思います。

この辺の話はSOLIDオブジェクト指向の原則を意識し始めると自然とそうなると思います。

2つ目

ちょっと回答がずれますが、継承よりinterfaceを利用することを意識しています。
何でもかんでも継承するとおっしゃるようにvirtualを付けた・付けないで問題が発生します。
継承を深くするより、機能を分断しクラスを設計し、それをまた集合・集約として利用する方が、
再利用性が高くなると思っています。

他の方もおっしゃっているようにクラスの粒度が大きい、単一責任の原則(SRP)に沿っていないのだと思います。

もう一つ、テストのためにコードが見にくくなることを諦めていません・諦めてはいけません。

ご参考になれば幸いです。

投稿2017/05/23 05:28

編集2017/05/23 05:32
Harurow

総合スコア14

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

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

lazex

2017/05/23 12:48

拡張メソッドを作る目的以外では すべてのクラスが interface をもち、 そこに static なメソッドが無い状態ということですよね クラスをどう分割してもどこかにでてくる「状態を考慮しない関数」をinstance methodにすることが避けたいものですが、それが「テストのため」でなく「交換可能」別の回答にある疎結合にすることにメリットを見い出せばそこまで気にならなくなるかもしれません interface は generics 使うとか必要にならなければあまり作らないものだったのですが、interface を勧めてくれる方が多いのでinterfaceについても考えてみます
guest

0

質問が2つあるなら、質問を2つ作成して欲しいです。
他の方があとで検索しやすくなりますし、適切にベストアンサーが着きますし、回答者もちゃんと評価されるので・・・

一つ目の回答です。

すべてをstatic methodで書いてはいけないかという点についてですが、それだったらC#の基本的な文法を否定して、変な書き方になるので、他の言語で書くとよいと思います。F#だったらC#より違和感がないかもしれません。

また、関数型言語の場合でも複雑な操作を行う関数は引数が多くなります。その為、バインドや関数渡しなどを使い引数を少なくしてわかりやすくします。Pythonは暗黙のself(this)を最初の引数としてつかっていることからも分かる通り、staticにしようとしまいと同じだけ引数をつかっているのです。(ノイマン型のCPUをつかっているので基本原理は同じと考えてもよいかもしれません。)

オブジェクトは副作用を持つという点について問題がありますが、その問題はオブジェクトをインスタンス後に状態変更出来なくするオブジェクトにするということでこのようなものをimmutableと呼びます。

immutableに出来なくても、できるだけ状態を持たないようにすることで複雑化を避けることができます。

また、javaでMOJOと呼ばれ、.netではPOCOと呼ばれるオブジェクトがあります。これは、継承とかややこしいオブジェクト設計は避けようという動きです。

テストの問題であれば、テストが複雑になる原因がそもそも”プログラムで解決しようとする問題”(以下、問題と呼ぶ)の複雑さにある場合があります。問題が複雑であればテストは複雑になるので、どのように問題を分解するべきかという事では無いでしょうか?

二つ目の回答は、モックを使う場合私は、インターフェイスを使います。現行のオブジェクトをインターフェイスに置き換えるのは面倒ですが、継承を使うとそれはそれでモックオブジェクトの作成が面倒では無いでしょうか。

投稿2017/05/20 01:16

iwamoto_takaaki

総合スコア2883

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

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

lazex

2017/05/21 12:29

やりたいことがしやすい言語を選ぶというのも可能であればありな方法ですね static にしようとしてるところは副作用なく参照透過なものなので、関数型な言語と言う選択も考えてみます staticであろうと引数のプロパティを変えて状態を変えるのならthisあるのと一緒というのはそのとおりですね 何が理想か、が自分の中でも定まらなくなってきました・・・ 2つめですが、インタフェースとvirtual化して継承のどちらも面倒で、テスト以外にインタフェースとして必要ないのにインタフェースができたり、有名らしいモックライブラリでvirtualが使われていたので継承の方を使いました
iwamoto_takaaki

2017/05/22 01:28

2つ目のvirtualの話ですが、すべてのメソッドにvirtualを付けるとしたらという前提の話でした。必要のないのにinterfaceを作る問題は、必要がないvirtualが大量につくのに比べたら”まし”だという感想です。 対象がすくなけれはvirtualでもいいかもしれませんが、わたしなら、クラス設計を見直し、このような箇所を局所化することを検討します。 テストが設計に影響するのは本末転倒と考えるかもしれませんが、私はテストが設計に良い影響を与えるという点について信じていて、何らかの境界がある部分にモックが必要なんだとおもっています。とはいえ実際のコードを見ないとなんとも言えないのですが・・・
guest

0

Pythonはどちらも該当しなさそうです。

ひとつめ
最近のバージョンのPythonであればmockライブラリが標準で入っているので、テストを意識せずに好きに書いたコードでもシンプルに実装されていれば簡単にテストできます。

ふたつめ
Pythonはクラス名.メソッド名として実行するべきメソッドを明示的に指定できますし、逆にダックタイピングで実行対象を制限せずにself.メソッド名ともできます。ので、実装時にテストのことを考える必要はこの点でもありません。

投稿2017/05/20 01:13

YouheiSakurai

総合スコア6142

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

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

lazex

2017/05/20 15:02

Pythonは本格的に使ったことがなくテストの知識はほぼないのですが、 言語自体がテストを意識しないようになっているものはいいですね ふたつめですが、クラス名でなくselfにしておけばオーバーライドされていればそっちが使われる ということですよね やはり継承しても親クラスに手を入れて置かなければ真にオーバーライドできないのはC#くらいの特殊なしくみですよね
honami

2017/05/22 01:39

テスト機構で言えばPythonにはdoctestなんてものも標準で用意されており、この辺りは言語の思想を強く表していますね。
lazex

2017/05/23 13:08

ドキュメントにサンプルとしてテスト書いておくというのは変わってますよね 関数が長くなるデメリットがありますが、使い方を知りたいときに別のテストファイルを探さなくてもいいメリットもありますし、引数も返り値もシンプルな型ならありかなって思います
guest

0

staticメソッドのテストはpower mockなどのフレームワーク使えば良いだけの話ですね。

投稿2017/05/19 15:36

harashow1701

総合スコア854

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

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

lazex

2017/05/20 14:26

Javaだと power mock でstaticメソッドも置き換えれるのですね アプリケーション側はそのままでテスト側だけで対処できるのはいいですね
guest

0

C#でしたら、Fakes FrameworkやPrigを検討してみてはどうでしょうか。
staticだろうがモックできるようになりますよ。

投稿2017/05/23 23:53

bleis-tift

総合スコア19

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

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

lazex

2017/05/24 12:29

ILレベルで書き換えするからstaticもモックできるというのはちらっとみたことありましたが これのことですかね 使い方を見てみましたが、導入はやはり一手間かかりますね 既存コードを書き換える手間が無いという意味ではstaticをモック化できるのが楽なので検討してみます
guest

0

C#のモックライブラリーなどは詳しくないため、1つ目の問いにのみとなります。

既出かも知れませんが、テストではなく、オブジェクト指向設計というものが、オブジェクト(モジュール)間の結合強度を下げて、疎結合化するという目的を持った設計技法なので、基本としてインスタンスメソッドを作るようにしています。
(例外はありますが)

疎結合化して、同じ操作でも、オブジェクト毎に異なる振る舞いを行うことを、ポリモーフィズム(多態性)といいますが、これは、オブジェクトを設計する側ではなくて、使う側にとってメリットがあります。

例えば、ウィンドウ制御を行うGUIアプリを作成するような場合を例に挙げると、以下のようなことが出来ます。

Windowというオブジェクトがあるとします。
Windowを閉じる機能として、Closeという要求操作(メソッドなり関数なり、呼び方は言語によってまちまちです)があるとします。
これが、WebブラウザーのWindowなのか、はたまたテキストエディターのWindowなのかに関係なく、ウィンドウ制御側は、CloseというAPIを経由して、閉じたいWindowオブジェクトにウィンドウを閉じるように要求することが出来ます。
これが、所謂インスタンスメソッドといった呼び方をされるものです。

密結合でいいのであれば、ウィンドウ制御側は、Webブラウザーのウィンドウを閉じる操作(=staticメソッド)を呼び出したり、テキストエディターのウィンドウを閉じる操作をしたりしなければなりません。

適当なコードで表現してみると、インスタンスメソッドの場合、
(ちょっとだけC#風にしてみました)

Window window = new WebBrowser();
window.Close(); // ここではWindowのCloseを呼び出している
window = new TextEditor(); // window変数に破壊的再代入していますが、多態性のサンプルとして必要なのでご了承下さい(iPhoneで書くの大変なので)
window2.Close(); // ここではWindowのCloseを呼び出している

と出来ます。

対して、staticメソッドの場合、

WebBrowser.Close();
TextEditor.Close();

としなければなりません。

モック云々で言えば、モックライブラリー次第なので、staticメソッドだろうとモック化出来るので、テストのためではなく、汎用性を高めたり、疎結合化することで、不具合が出たときにモジュール差し替えるだけで修正出来る(Windowsのdllなんかをイメージするといいかも。Javaもクラスファイルの差し替えで同じこと出来ますし)のが、オブジェクト指向設計の基本的な考え方のひとつだったりします。

staticメソッドにも利点はありますが、staticメソッドは構造化設計の考え方とも言われたりする(使い方次第ではありますが)ので、オブジェクト指向言語で設計する際には、基本的には先ずはinterface(言語によってはtraitだったりprotocolといった機能)を用意して、そこに対して実装を行うようにしています。

長いのと、少し古いですが、次のスライドを読むと、疎結合化する意味などが掴みやすくなるのではないかと思います。(相当後のほうのスライドですが)
https://www.slideshare.net/mobile/MoriharuOhzu/ss-14083300

staticメソッドにも利点はあって、所謂Factoryメソッドパターンや、言語的にシングルトンオブジェクトパターンをサポートしていない言語で、シングルトンオブジェクトパターンを実現するような場合には、オブジェクト指向設計でも有用だったりします。

投稿2017/05/23 13:15

poad1010

総合スコア30

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問