メソッドの再利用可能性をどのように知ることができますか?[閉まっている]


133

私は家で自分のビジネスを気にしていると私の妻は私に来て、言う

Honey ..コンソールで2018年の世界中の夏時間をすべて印刷できますか?何か確認する必要があります。

そして、Javaの経験で生涯待っていたものが次のように思いついたので、とても幸せです。

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(2018, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (2018 == now.getYear()) {
                    int hour = now.getHour();
                    now = now.plusHours(1);
                    if (now.getHour() == hour) {
                        System.out.println(now);
                    }
                }
            }
        );
    }
}

しかし、彼女は、私が倫理的に訓練されたソフトウェアエンジニアであるかどうかを私にテストしているだけで、私はそれ以来ではないようだと言います(ここから取られます)。

倫理的に訓練されたソフトウェアエンジニアがDestroyBaghdadの手順を作成することに同意することはありません。代わりに、基本的な職業倫理では、バグダッドをパラメータとして与えることができるDestroyCityプロシージャを記述することを求めます。

そして私は、あなたが私を得た。..任意の渡しOK、罰金、のようですあなたが好き、ここにあなたが行きます:

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings(int year) {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(year, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (year == now.getYear()) {
                    // rest is same..

しかし、パラメーター化する量(および量)をどのように知ることができますか?結局のところ、彼女は言うかもしれない..

  • 彼女はカスタム文字列フォーマッタを渡したいと思っていますが、私が既に印刷している形式が気に入らないのかもしれません。 2018-10-28T02:00+01:00[Arctic/Longyearbyen]

void dayLightSavings(int year, DateTimeFormatter dtf)

  • 彼女は特定の月の期間だけに興味がある

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)

  • 彼女は特定の時間帯に興味がある

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)

具体的な質問を探している場合:

がの場合destroyCity(City city)よりも優れている場合destroyBaghdad()takeActionOnCity(Action action, City city)さらに優れていますか?なぜですか?

結局のところ、最初にAction.DESTROYthen Action.REBUILDで呼び出すことができますよね?

しかし、都市で行動を起こすだけでは十分ではありませんtakeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)。結局、私は電話したくない:

takeActionOnCity(Action.DESTORY, City.BAGHDAD);

それから

takeActionOnCity(Action.DESTORY, City.ERBIL);

私ができるときなど:

takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);

ps私が言及した引用に基づいて質問を作成しただけで、私は世界のどの国、宗教、人種、または何に対しても何もしません。私はちょうどポイントを作ろうとしています。



71
ここであなたが主張している点は、私が何度も表明しようとしたことです。しかし、それはそれよりも深くなります。プログラミング言語は、ある種の一般性を他よりも簡単にするためにデザイナーによって作成され、開発者としての私たちの選択に影響を与えます。簡単な値でメソッドをパラメータ化し、そしてそれは、あなたのツールボックスを持っている最も簡単なツールだとき、誘惑にかかわらず、それはユーザーのために理にかなっているかどうか、それを使用することです。
エリックリッパー

30
再利用は、あなた自身が望むものではありません。コードアーティファクトは構築するのに費用がかかるため、可能な限り多くのシナリオで使用して、それらのシナリオ全体でそのコストを償却する必要があると考えているため、再利用を優先します。この信念はしばしば観察によって正当化されないため、再利用性を設計するためのアドバイスはしばしば誤用されます。コードを設計して、アプリケーションの総コストを削減します
エリックリッパー

7
あなたの妻は、あなたに嘘をついて時間を無駄にしている非倫理的な人です。彼女は答えを求め、提案された媒体を与えました。その契約により、その出力をどのように取得するかは、あなたとあなた自身の間だけです。また、destroyCity(target)方法よりも非倫理的ですdestroyBagdad()!世界の都市はもちろんのこと、都市を一掃するためのプログラムを書いているのはどのようなモンスターですか?システムが侵害された場合はどうなりますか?!また、時間/リソース管理(投資の努力)は倫理と何の関係がありますか?口頭/書面による契約が合意どおりに完了した限り。
テズラ

25
このジョークを読みすぎているのではないかと思います。コンピュータープログラマーがいかに悪い倫理的決定を下すようになるかについてのジョークです。なぜなら、彼らは人間に対する仕事の影響よりも技術的な考慮事項を優先するからです。プログラムの設計に関する良いアドバイスを意図したものではありません。
エリックリッパー

回答:


114

それはずっとカメです。

またはこの場合の抽象化。

グッドプラクティスコーディングは無限に適用できるものであり、ある時点で抽象化のために抽象化しているということです。その行を見つけることは、環境に大きく依存するため、経験則に簡単に入れることはできません。

たとえば、単純なアプリケーションを最初に要求し、その後拡張を要求することが知られている顧客がいました。また、何が欲しいのかを尋ねる顧客がいて、通常、拡張のために戻ってくることはありません。
アプローチは顧客ごとに異なります。最初の顧客については、将来このコードを再検討する必要があることを合理的に確信しているため、先制的にコードを抽象化することで費用がかかります。2番目の顧客については、アプリケーションを任意の時点で拡張したくないと思われる場合、その余分な労力を投資したくない場合があります(注:これは、良い慣行に従わないという意味ではなく、単に現在必要な以上のことは避けてください。

実装する機能を知るにはどうすればよいですか?

私が上記に言及した理由は、あなたがすでにこのtrapに陥っているからです。

しかし、パラメーター化する量(および量)をどのように知ることができますか?結局、彼女は言うかもしれません

「彼女は言うかもしれない」は、現在のビジネス要件ではありません。将来のビジネス要件の推測です。一般的なルールとして、推測に基づいて判断するのではなく、現在必要なものだけを開発してください。

ただし、ここではコンテキストが適用されます。私はあなたの妻を知りません。たぶん、あなたは彼女が実際にこれを望んでいることを正確に判断したかもしれません。ただし、これは本当に彼らが望むものであることを顧客に確認する必要があります。

実装するアーキテクチャを知るにはどうすればよいですか?

これは難しいです。顧客は内部コードを気にしないので、必要かどうかを尋ねることはできません。問題に関する彼らの意見はほとんど無関係です。

ただし、顧客に適切な質問をすることで、その必要性を確認できます。アーキテクチャについて尋ねるのではなく、将来の開発やコードベースの拡張に対する期待について尋ねてください。また、必要な時間枠で派手なアーキテクチャを実装できない場合があるため、現在の目標に期限があるかどうかを尋ねることもできます。

コードをさらに抽象化するタイミングを知るにはどうすればよいですか?

私は(誰もが知っている場合、私に知らせて、私は信用を与えるでしょう)それを読んだところ、私は知りませんが、親指の良いルールは、開発者が穴居人のようにカウントしなければならないことである:1、2、多くの

ここに画像の説明を入力してください XKCD#764

言い換えると、特定のアルゴリズム/パターンが3に使用される場合、再利用可能(= 度も使用可能)になるように抽象化する必要があります。

明確にするために、使用されているアルゴリズムのインスタンスが2つしかない場合は、再利用可能なコードを書くべきではないことを暗示していません。もちろんこれも抽象化できますが、3つのインスタンスについて抽象化する必要があるというルールにする必要があります。

繰り返しますが、これはあなたの期待の要因です。3つ以上のインスタンスが必要であることを既に知っている場合は、もちろんすぐに抽象化できます。しかし、それをもっと何度も実装したいと思うだけなら、抽象化の実装の正確さは、推測の正確さに完全に依存します。
正しく推測できれば、時間を節約できます。間違って推測した場合、時間と労力の一部を無駄にし、アーキテクチャを危険にさらして、必要のないものを実装する可能性があります。

がの場合destroyCity(City city)よりも優れている場合destroyBaghdad()takeActionOnCity(Action action, City city)さらに優れていますか?なぜですか?

それは非常に多くのことに依存しています:

  • どの都市で実行できる複数のアクションがありますか?
  • これらのアクションは同じ意味で使用できますか?なぜなら、「破棄」アクションと「再構築」アクションの実行が完全に異なる場合、2つのtakeActionOnCityメソッドを1つのメソッドにマージしても意味がないからです。

また、これを再帰的に抽象化すると、別のメソッドを実行するためのコンテナーにすぎないほど抽象的であるメソッドになります。つまり、メソッドを無関係で無意味にしていることに注意してください。メソッド本体
全体takeActionOnCity(Action action, City city)がに過ぎないaction.TakeOn(city);場合、takeActionOnCityメソッドに本当に目的があるのか​​、それとも価値のないものを追加する単なるレイヤーではないのか疑問に思うはずです。

しかし、都市で行動を起こすだけでは十分ではありませんtakeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)

同じ質問がここに表示されます:

  • 地理的な地域のユースケースはありますか?
  • 都市と地域でのアクションの実行は同じですか?
  • どの地域/都市でも行動を起こすことができますか?

3つすべてに「はい」と明確に答えられる場合は、抽象化が必要です。


16
「1つ、2つ、多く」のルールを十分に強調することはできません。/ parametrizeを抽象化する無限の可能性がありますが、有用なサブセットは小さく、多くの場合ゼロです。どのバリアントに価値があるかを正確に知ることは、ほとんどの場合、振り返って判断することしかできません。したがって、当面の要件*に固執し、新しい要件や後知恵によって必要に応じて複雑さを追加してください。*問題の領域をよく知っている場合は、明日必要だとわかっているので、何かを追加しても大丈夫かもしれません。しかし、この力を賢く使うと、破滅につながる可能性もあります。
クリスチャンザウアー

2
>「どこで読んだかわかりません[..]」。あなたはコーディングホラー:三人のルールを読んでいたかもしれません。
ルーン

10
「1つ、2つ、多くのルール」は、DRYを盲目的に適用することによって間違った抽象化を構築するのを防ぐために本当にあります。実は、2つのコードはほとんど同じように見えるため、違いを抽象化したくなるのです。しかし、初期段階では、コードのどの部分が安定しているか、どの部分が安定していないかがわかりません。さらに、実際には独立して進化する必要があることがわかります(異なる変化パターン、異なる責任のセット)。どちらの場合でも、間違った抽象化があなたに対して働き、邪魔になります。
フィリップミロヴァーノヴィッチ

4
「同じロジック」の3つ以上の例を待つことにより、抽象化する対象と方法をより適切に判断できます(実際、異なる変更パターンを持つコード間の依存関係/カップリングについてです)。
フィリップミロヴァーノヴィッチ

1
@kukis:現実的な線は2で描画する必要があります(Baldrickkのコメントによる):ゼロ対1(データベースリレーションの場合)。しかし、これは不必要なパターン探索行動への扉を開きます。2つのことはあいまいに見えるかもしれませんが、実際に同じであるという意味ではありません。ただし、3番目のインスタンスが1番目と2番目のインスタンスの両方に似たほつれに入った場合、それらの類似性が実際に再利用可能なパターンであるというより正確な判断を下すことができます。したがって、常識の線は3で描かれ、2つのインスタンス間で「パターン」を見つけたときのヒューマンエラーを考慮します。
18年

44

練習

これはSoftware Engineering SEですが、ソフトウェアの作成はエンジニアリングよりも芸術です。どれだけの再利用性で十分かを把握するために、従うべき普遍的なアルゴリズムや測定がありません。何でもそうであるように、プログラムを設計する練習が多ければ多いほど、あなたはそれをより良く得るでしょう。パラメータ設定が多すぎるか少なすぎると、何が間違っているのか、どのように間違っているのかがわかるので、「十分」なものをよりよく感じることができます。

しかし、それは今ではあまり役に立たないので、いくつかのガイドラインはどうですか?

あなたの質問を振り返ってください。「彼女は言うかもしれない」と「私はできる」がたくさんあります。将来の必要性について理論化した多くの声明。人間は未来を予測するのに悪口を言う。そして、あなた(おそらく)は人間です。ソフトウェア設計の圧倒的な問題は、あなたが知らない未来を説明しようとしています。

ガイドライン1:必要ない

真剣に。やめて 多くの場合、その想像された将来の問題は現れません-そしてあなたがそれを想像したようにそれは確かに現れません。

ガイドライン2:費用/便益

かっこいい、その小さなプログラムは多分書くのに数時間かかった?それで、もしあなたの妻戻ってきて、それらのことを求めたらどうしますか?最悪の場合、あなたはそれを行うために別のプログラムを一緒に投げるのにさらに数時間を費やします。この場合、このプログラムをより柔軟にするのに時間はかかりません。そして、それはランタイムの速度やメモリ使用量にあまり追加しません。しかし、重要なプログラムにはさまざまな答えがあります。異なるシナリオには異なる答えがあります。ある時点で、将来の語りのスキルが不完全であっても、コストは明らかに価値がありません。

ガイドライン3:定数に焦点を当てる

質問を振り返ってください。元のコードには、多くの定数があります。20181。定数int、定数文字列...これらは定数ない必要がある可能性が最も高いものです。さらに良いことに、パラメータ化するのに少しの時間しかかかりません(または、少なくとも実際の定数として定義します)。しかし、注意すべきもう1つのことは、一定の動作です。のSystem.out.println例えば。使用に関するこの種の仮定は、将来変更されるものである傾向があり、修正するには非常にコストがかかる傾向があります。それだけでなく、このようなIOにより、関数が不純になります(タイムゾーンの取得もいくらか)。その動作をパラメータ化すると、関数がより純粋になり、柔軟性とテスト容易性が向上します。最小限のコストで大きなメリット(特にSystem.out、デフォルトで使用するオーバーロードを作成する場合)。


1
それは単なるガイドラインであり、1は問題ありませんが、それらを見て、「これは変わるのですか?」また、printlnは、高次関数でパラメーター化できますが、Javaはそれらに優れていません。
テラスティン

5
@KorayTugay:プログラムが本当に家に帰ってくるあなたの妻のためのものだったら、YAGNIはあなたの初期バージョンは完璧だとあなたに言うでしょう、そして定数やパラメータを導入するためにこれ以上時間を投資すべきではありません。YAGNIにはコンテキストが必要です-あなたのプログラムは使い捨ての ソリューションなのでしょうか、それとも移行プログラムは数か月しか実行されていないのですか、それとも数十年にわたって使用および保守されることを目的とした巨大なERPシステムの一部ですか?
ドックブラウン

8
@KorayTugay:I / Oを計算から分離することは、基本的なプログラム構造化手法です。データの表示からのデータの消費からのデータの変換からのデータのフィルタリングからデータの生成を分離します。いくつかの機能的なプログラムを勉強する必要があります。そうすれば、これをより明確に確認できます。関数型プログラミングでは、無制限の量のデータを生成し、関心のあるデータのみをフィルタリングし、データを必要な形式に変換し、そこから文字列を構築し、この文字列を5つの異なる関数で印刷することは非常に一般的です、各ステップに1つ。
ヨルグWミットタグ

3
補足として、YAGNIに強く従うと、継続的なリファクタリングが必要になります。「継続的なリファクタリングなしで使用すると、技術的負債として知られる組織化されていないコードと大規模なリワークにつながる可能性があります。」したがって、YAGNIは一般的に良いことですが、すべての開発者/企業が喜んで行うことではないコードの再検討と再評価という大きな責任が伴います。
フラット

4
@Telastyn:「これは変わらないので、定数に名前を付けなくてもコードの意図は簡単に読めるのでしょうか?」に質問を拡張することをお勧めします。
18年

27

まず、セキュリティを重視するソフトウェア開発者は、何らかの理由で認証トークンを渡さずにDestroyCityメソッドを作成しません。

私も、他の文脈に当てはまらない明白な知恵を持っている命令として何かを書くことができます。文字列の連結を許可する必要があるのはなぜですか?

第二に、実行されるすべてのコードは完全に指定する必要があります

決定が所定の場所にハードコーディングされているか、別のレイヤーに延期されているかは関係ありません。ある時点で、破壊されるものとその指示方法の両方を知っているコードの一部の言語があります。

それは同じオブジェクトファイルdestroyCity(xyz)にある可能性があり、設定ファイルdestroy {"city": "XYZ"}"にある可能性があります:または、UIでの一連のクリックとキー押下である可能性があります。

第三に:

Honey ..コンソールで2018年の世界中の夏時間をすべて印刷できますか?何か確認する必要があります。

以下とは非常に異なる要件のセットです。

彼女はカスタム文字列フォーマッタを渡したい、...特定の月の期間のみに興味がある、... [特定の時間の期間に興味がある]

現在、2番目の要件セットは明らかに、より柔軟なツールになっています。対象となる対象者がより広く、アプリケーションの領域がより広い。ここでの危険性は、世界で最も柔軟なアプリケーションが実際にはマシンコードのコンパイラであることです。それは文字通り非常に汎用的なプログラムであり、コンピュータを必要なものにするために(ハードウェアの制約内で)何でも作成することができます。

一般的に、ソフトウェアを必要とする人々は一般的なものを望んでいません。彼らは何か特別なものが欲しいのです。より多くのオプションを与えることで、実際には彼らの生活はより複雑になります。彼らがその複雑さを望んでいるなら、彼らは代わりにコンパイラを使用し、あなたに尋ねることはないでしょう。

あなたの妻は機能を求めていて、あなたに彼女の要件を過小評価していました。この場合、それは一見意図的であり、一般的には、彼らがそれ以上良く知らないからです。それ以外の場合は、コンパイラ自体を使用しただけです。そのため、最初の問題は、彼女が何をしたいのかについての詳細を尋ねなかったことです。彼女はこれを数年にわたって実行したいと思いましたか?彼女はそれをCSVファイルにしたかったのですか?あなたは、彼女が自分で何をしたいのか、そして彼女があなたに彼女を見つけて決定するようあなたに求めているものを見つけませんでした。どの決定を延期する必要があるかがわかったら、パラメーター(およびその他の構成可能な手段)を使用してそれらの決定を伝達する方法を理解できます。

そうは言っても、ほとんどのクライアントは、本当に自分自身を作りたいと思う、または本当に作りたくないという特定の詳細(別名、決定)について、疎通、推測、または無知です(しかし、それは素晴らしいように聞こえます)。これが、PDSA(plan-develop-study-act)のような作業方法が重要な理由です。要件に沿って作業を計画し、一連の決定(コード)を作成しました。今、あなた自身またはあなたのクライアントと一緒にそれを勉強し、新しいことを学ぶ時が来ました。これらはあなたの思考を前進させます。最後に、新しい洞察に基づいて行動します-要件を更新し、プロセスを改善し、新しいツールを入手します...そして、再び計画を開始します。これは、時間の経過とともに隠れた要件を明らかにし、多くのクライアントに進歩を証明します。

最後に。あなたの時間は重要です。それは非常に現実的で非常に有限です。あなたが下すすべての決定は、他の多くの隠れた決定を必要とし、これがソフトウェア開発の目的です。引数として決定を遅らせると、現在の関数は単純になりますが、他の場所はより複雑になります。その決定は、他の場所に関連していますか?ここでより関連性がありますか?誰が本当に決断するのですか?あなたはこれを決めています。これはコーディングです。一連の決定を頻繁に繰り返す場合、それらを何らかの抽象化の中で体系化することには非常に大きな利点があります。XKCDには、ここで役立つ視点があります。そして、これはシステムのレベルで機能、モジュール、プログラムなどに関連しています。

最初のアドバイスは、関数に決定権がない決定を引数として渡すことを意味します。問題は、DestroyBaghdad関数が実際にその権利を持つ関数である可能性があることです。


+1コンパイラに関する部分が大好きです!
リー

4

ここには多くの長い答えがありますが、正直なところ、それは非常に簡単だと思います

関数に含まれるハードコーディングされた情報は、関数名の一部ではありませんが、パラメーターである必要があります。

あなたの機能で

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
            LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
            ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
            while (2018 == now.getYear()) {
                int hour = now.getHour();
                now = now.plusHours(1);
                if (now.getHour() == hour) {
                    System.out.println(now);
                }
            }
        });
    }
}

あなたが持っている:

The zoneIds
2018, 1, 1
System.out

したがって、これらすべてを何らかの形でパラメーターに移動します。zoneIdsは関数名に暗黙的であると主張することができますが、「DaylightSavingsAroundTheWorld」などに変更することでさらにそれを実現できます。

書式文字列がないので、書式文字列を追加することは機能要求であり、妻に家族のJiraインスタンスを参照する必要があります。バックログに入れて、適切なプロジェクト管理委員会で優先順位を付けることができます。


1
あなたは(自分自身をOPに向けて)フォーマット文字列を追加すべきではありません。何も印刷すべきではないからです。このコードの再利用を完全に妨げる1つのことは、印刷することです。ゾーン、またはゾーンのマップがDSTから外れた時点に戻る必要があります。(行くとき、それは唯一の識別理由がオフ DST、およびない DST、私は理解していないことが問題の記述と一致していないようです。。)
デヴィッド・コンラッド

要件はコンソールに印刷することです。私は提案として、あなたは、パラメータとして出力ストリームに渡すことで、タイトなcouplungをmitgateすることができます
ユアン・

1
それでも、コードを再利用可能にする場合は、コンソールに印刷しないでください。結果を返すメソッドを作成し、それらを取得して印刷する呼び出し元を作成します。また、テスト可能になります。出力を生成したい場合は、出力ストリームを渡さず、Consumerを渡します。
デビッドコンラッド

出力ストリームは消費者です
ユアン

いいえ、OutputStreamConsumerではありません。
デビッドコンラッド

4

要するに、エンドユーザーが関数を再利用できるかどうか気にしないので、ソフトウェアを再利用可能にするように設計しないでください。代わりに、設計の理解を容易にするエンジニア-私のコードは他の誰かや私の将来の忘れっぽい自分にとっても理解しやすいのでしょうか?-および設計の柔軟性-必然的にバグの修正、機能の追加、または機能の変更が必要になった場合、コードはどの程度変更に抵抗しますか?顧客が気にする唯一のことは、顧客がバグを報告したり、変更を要求したときにどれだけ迅速に対応できるかです。偶然にデザインに関するこれらの質問をすると、再利用可能なコードが生成される傾向がありますが、このアプローチでは、そのコードの寿命にわたって直面する実際の問題を回避することに集中し続けるため、高額で非実用的なことを追求するのではなく、エンドユーザーにより良いサービスを提供できます首のひげを喜ばせる「エンジニアリング」の理想。

あなたが提供した例のような単純なものでは、最初の実装はどれだけ小さいかは問題ありませんが、機能の柔軟性(設計の柔軟性とは対照的に)を詰め込むと、この単純な設計は理解しにくくなり、脆弱になります1つの手順。以下は、わかりやすさと柔軟性のために複雑なシステムを設計するための私の好ましいアプローチの説明です。1つの手順で20行未満で記述できるものにはこの戦略を使用しません。なぜなら、非常に小さいものが、そのままで理解性と柔軟性の基準をすでに満たしているからです。


プロシージャではなくオブジェクト

ソフトウェアが行うべきことを実行するために呼び出す多くのルーチンで旧式のモジュールのようなクラスを使用するのではなく、手近なタスクを達成するために相互作用し協力するオブジェクトとしてドメインをモデリングすることを検討してください。オブジェクト指向のパラダイムのメソッドは、元々、オブジェクト間の信号として作成されたため、そのことを実行Object1するよう指示Object2し、場合によっては戻り信号を受信できます。これは、オブジェクト指向パラダイムは本質的に、ドメインオブジェクトとその相互作用のモデリングに関するものであり、命令型パラダイムの同じ古い機能と手順を整理するための派手な方法ではないためです。の場合void destroyBaghdadたとえば、バグダッドまたはその他のもの(すぐに複雑になり、理解しにくく、脆くなる)の破壊を処理するために、コンテキストなしの汎用メソッドを作成しようとする代わりに、破壊できるすべてのものが、自分自身を破壊する。たとえば、破壊できるものの動作を記述するインターフェイスがあります。

interface Destroyable {
    void destroy();
}

次に、このインターフェイスを実装する都市があります:

class City implements Destroyable {
    @Override
    public void destroy() {
        ...code that destroys the city
    }
}

インスタンスの破壊を要求Cityするものは、それがどのように発生するかを気にすることはないので、そのコードがの外部に存在する理由はなくCity::destroy、実際、City外部の内部動作の詳細な知識は、あなたがこれらの外部の要素を考慮しなければならないので、felxibilityはの動作を変更する必要がありますCity。これがカプセル化の背後にある真の目的です。すべてのオブジェクトには独自のAPIがあり、必要なことは何でもできるようにして、リクエストを処理することを心配できるようにしてください。

「制御」ではなく委任

さて、あなたの実装クラスがそうであるかどうかは、都市を破壊するプロセスがどれほど一般的であるCityBaghdadによって決まります。すべての確率で、a Cityは都市の完全な破壊を達成するために個別に破壊する必要がある小さな破片で構成されます。その場合、それらの破片のそれぞれも実装しDestroyable、それぞれCity破壊するように指示されます外から誰かが自分自身Cityを破壊するように要求したのと同じ方法で自分自身。

interface Part extends Destroyable {
    ...part-specific methods
}

class Building implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
       ...code to destroy a building
    }
}

class Street implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
        ...code to destroy a building
    }
}

class City implements Destroyable {
    public List<Part> parts() {...}

    @Override
    public void destroy() {
        parts().forEach(Destroyable::destroy);            
    }
}

本当に夢中になりBomb、場所にドロップされ、特定の半径内のすべてを破壊するというアイデアを実装したい場合は、次のようになります。

class Bomb {
    private final Integer radius;

    public Bomb(final Integer radius) {
        this.radius = radius;
    }

    public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
            grid,
            target,
            this.radius
        ).forEach(Destroyable::destroy);
    }
}

ObjectsByRadiusは、Bomb入力から計算されるオブジェクトのセットを表しBombます。これは、オブジェクトを処理できる限り、計算がどのように行われるかを気にしないからです。これは偶発的に再利用可能ですが、主な目標はBomb、オブジェクト全体を落としたり破壊したりするプロセスから計算を分離することです。

アルゴリズムではなく相互作用

複雑なアルゴリズムの適切な数のパラメーターを推測しようとする代わりに、プロセスを相互作用するオブジェクトのセットとしてモデル化する方がより理にかなっています。それぞれが非常に狭い役割を持ちます。これらの明確に定義された、理解しやすく、ほとんど変わらないオブジェクト間の相互作用を通じて処理します。正しく行われた場合、これにより、インターフェイスを1つまたは2つ実装し、main()メソッドでインスタンス化されるオブジェクトを修正するのと同じくらい簡単な、最も複雑な変更の一部を行うことができます。

元の例に何かを挙げたいと思いますが、正直に言うと、「印刷...夏時間」とはどういう意味かわかりません。このカテゴリの問題について言えることは、計算を実行しているときはいつでも、その結果をさまざまな方法でフォーマットできるということです。これを分解するための好ましい方法は次のとおりです。

interface Result {
    String print();
}

class Caclulation {
    private final Parameter paramater1;

    private final Parameter parameter2;

    public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
    }

    public Result calculate() {
        ...calculate the result
    }
}

class FormattedResult {
    private final Result result;

    public FormattedResult(final Result result) {
        this.result = result;
    }

    @Override
    public String print() {
        ...interact with this.result to format it and return the formatted String
    }
}

この例では、この設計をサポートしていないJavaライブラリのクラスを使用しているため、単にAPIをZonedDateTime直接使用できます。ここでの考え方は、各計算が独自のオブジェクト内にカプセル化されるということです。実行する回数や結果のフォーマット方法については想定していません。これは、計算の最も単純な形式の実行にのみ関係します。これにより、理解しやすく、柔軟に変更できます。同様に、Resultは計算の結果をカプセル化することFormattedResultのみに関係しResult、定義されたルールに従ってフォーマットするために対話することのみに関係します。この方法では、各メソッドには明確に定義されたタスクがあるため、各メソッドの引数の完全な数を見つけることができます。また、インターフェイスが変更されない限り(オブジェクトの責任を適切に最小化した場合は変更されない可能性が高い限り)、前進するように変更する方がはるかに簡単です。私たちのmain()方法は次のようになります。

class App {
    public static void main(String[] args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
            System.out.println(
                new FormattedResult(
                    new Calculation(
                        set.get(0),
                        set.get(1)
                    ).calculate()
                ).print()
            );
        });
    }
}

実際、オブジェクト指向プログラミングは、命令型パラダイムの複雑さ/柔軟性の問題に対するソリューションとして特別に発明されました。イディオム内で命令型の関数とプロシージャを指定します。


これは非常に詳細で考え抜かれた答えですが、残念ながら、OPが本当に求めていたもののマークを見逃していると思います。彼は、具体的な例を解決するための優れたOOPプラクティスに関するレッスンを求めているのではなく、ソリューションへの時間投資と一般化を決定する基準について尋ねていました。
maple_shaft

@maple_shaft多分私はマークを逃しましたが、私もあなたが持っていると思います。OPは、時間対一般化の投資について質問しません。彼は、「私のメソッドがどのように再利用可能であるかをどのようにして知るのですか?」と尋ねます。彼は質問の本文で続けて、「destroyCity(City city)がdestroyBaghdad()よりも優れている場合、takeActionOnCity(Action action、City city)はさらに優れていますか?なぜ/なぜですか?」エンジニアリングソリューションへの代替アプローチのケースを作成し、メソッドを作成するための一般的な方法を見つけ出すという問題を解決すると信じ、私の主張をサポートする例を提供しました。気に入らなかったのが残念です。
-Stuporman

@maple_shaft率直に言って、私たちの答えが彼の質問の意味に関連するかどうかを判断できるのはOPだけです。
-Stuporman

@maple_shaftイントロを追加して、それが質問にどのように関係するかを明確にし、答えと実装例の間に明確な線引きを提供しました。それは良いですか?
Stuporman

1
正直なところ、これらの原則をすべて適用すると、答えは自然になり、スムーズで、非常に読みやすくなります。さらに、大騒ぎせずに変更可能。私はあなたが誰なのか分かりませんが、もっと多くのあなたがいればいいのに!私はまともなオブジェクト指向のリアクティブコードをリッピングし続けますが、それは常に半分のサイズで、より読みやすく、より制御しやすく、スレッド化/分割/マッピングがあります。Reactは、今挙げた「基本的な」概念を理解していない人向けだと思います。
スティーブンJ

3

経験ドメイン知識、およびコードレビュー。

また、経験分野の知識チームの量に関係なく、必要に応じリファクタリングする必要を避けることはできません


Experienceを使用すると、作成するドメイン固有でないメソッド(およびクラス)のパターンを認識し始めます。そして、DRYコードにまったく興味があるなら、将来のバリエーションを書くことを本能的に知っているメソッドを書くことについて、悪い気持ちを感じるでしょう。そのため、代わりにパラメータ化された最小公分母を直感的に記述します。

(このエクスペリエンスは、ドメインオブジェクトとメソッドにも直感的に移行する場合があります。)

Domain Knowledgeを使用すると、どのビジネスコンセプトが密接に関連しているか、どのコンセプトに変数があるか、かなり静的であるかなどを把握できます。

コードレビューでは、実稼働コードになる前にパラメーターの過不足がキャッチされる可能性が高くなります。これは、ピアが(願わくば)ドメインコーディングの両方で独自の経験と展望を持つためです。


とは言っても、新しい開発者は通常、これらのSpidey Sensesや経験豊富な仲間がすぐに頼ることはありません。また、経験豊富な開発者でさえ、新しい要件を介して、または頭が曇っている日々をガイドするための基本的な規律の恩恵を受けます。だから、ここに私がスタートとして提案するものがあります:

  • 最小限のパラメータ設定で、素朴な実装から始めます。
    (明らかに必要なことがわかっいるパラメーターを含めてください...)
  • マジックナンバーとマジックストリングを削除し、構成やパラメーターに移動します
  • 「大きな」メソッドを小さく、適切な名前のメソッドに分解する
  • 非常に冗長な方法(便利な場合)を共通の分母にリファクタリングし、違いをパラメーター化します。

これらの手順は、必ずしも指定された順序で発生するとは限りません。既存のメソッド非常に冗長であること既にわかっているメソッドを記述するために座っている場合、便利な場合はリファクタリングに直接ジャンプします。(リファクタリングに2つのメソッドを記述、テスト、および維持するよりも大幅に長い時間をかけない場合)

しかし、多くの経験などがあるだけでなく、かなり最小限のコードの乾燥をお勧めします。明らかな違反をリファクタリングすることは難しくありません。そして、あなたが熱心すぎるなら、「WET」に相当するものよりも読み、理解し、保守するのがさらに難しい「過剰乾燥」コードになる可能性があります。


2
だから正しい答えはありませんIf destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better?か?はい/いいえの質問ですが、答えはありませんよね?または、最初の仮定が間違っていて、destroyCity(City)必ずしも良くないかもしれず、実際に依存しますか?だから、そもそもパラメータなしで直接実装したので、私は倫理的に訓練されたソフトウェアエンジニアではないという意味ではありませんか?私が尋ねている具体的な質問に対する答えは何ですか?
コライトゥゲイ

あなたの質問は、いくつかの質問をします。タイトルの質問への答えは、「経験、ドメイン知識、コードレビュー...そして...リファクタリングを恐れないでください」です。具体的な「これらはメソッドABCの正しいパラメーターですか」という質問に対する答えは...「わかりません。なぜ尋ねているのですか?現在持っているパラメーターの数に何か問題がありますか?」それを修正します。」...詳細については、「POAP」を参照してください。自分がしていることをしている理由を理解する必要があります。
svidgen

つまり... destroyBaghdad()メソッドから一歩後退しましょう。コンテキストは何ですか?ゲームの終了によりバグダッドが破壊されるというビデオゲームですか?もしそうなら... destroyBaghdad()は完全に合理的なメソッド名/署名かもしれません
...-svidgen

1
だからあなたは私の質問で引用された引用に同意しませんよね?It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure.あなたがナサニエル・ボレンスタインの部屋にいた場合、あなたは彼が実際に依存していると主張し、彼の声明は正しくありませんか?多くの人が答えて時間とエネルギーを費やしているのは美しいことですが、具体的な答えはどこにもありません。スパイディセンス、コードレビュー..しかし、答えはis takeActionOnCity(Action action, City city) better? null何ですか?
コライトゥゲイ

1
@svidgenもちろん、これを余分な労力なしで抽象化する別の方法は、依存関係を逆にすることです-関数に都市のリストを返すようにします(元のコードの "println"など)。これは必要に応じてさらに抽象化することができますが、この変更自体が元の質問に追加された要件の約半分を処理します-あらゆる種類の悪いことを行うことができる1つの不純な関数の代わりに、関数がありますそれはリストを返し、呼び出し元は悪いことをします。
ルアーン

2

品質、使いやすさ、技術的負債などと同じ答え:

再利用可能としてあなた、ユーザー、1があることを、それらを必要とします

それは基本的に判断の呼び出しです-抽象化の設計と維持のコストがコスト(=時間と労力)で返済されるかどうかは、あなたがラインを節約するでしょう。

  • 「ダウンザライン」というフレーズに注意してください。ここにはペイオフの仕組みがあります。そのため、このコードをさらにどの程度作業するかによって異なります。例えば:
    • これは一回限りのプロジェクトですか、それとも長期にわたって徐々に改善される予定ですか?
    • あなたのデザインに自信がありますか、それとも次のプロジェクト/マイルストーンのためにそれを廃棄するか、そうでなければ劇的に変更しなければならないでしょうか?
  • 予測される利益は、将来を予測する能力(アプリの変更)にも依存します。場合によっては、アプリが予定している場所を合理的に確認できます。何度もできると思うが、実際にはできない。ここでの経験則は、YAGNIの原則3つ規則です。どちらも、今、あなたが知っていることから離れることを強調しています

1 これはコード構造であるため、この場合の「ユーザー」はソースコードのユーザーです。


1

フォローできる明確なプロセスがあります。

  • それ自体が「モノ」である単一の機能(つまり、どちらも半分が理にかなっていない機能の任意の分割ではない)の失敗したテストを記述します。
  • 絶対最小コードを記述して、1行以上ではなく緑を渡すようにします。
  • すすぎ、繰り返します。
  • (必要に応じて容赦なくリファクタリングします。テスト範囲が広いため、簡単にリファクタリングできます。)

これは-少なくとも一部の人々の意見では-最適なコードです。可能な限り小さいため、完成した各機能は可能な限り短い時間で済みます(完成品を見ると、そうでない場合もあります)リファクタリング後の製品)、およびそれは非常に良いテストカバレッジを持っています。また、過剰に設計された汎用的なメソッドまたはクラスを著しく回避します。

また、これにより、物事を一般化する場合と特化する場合の非常に明確な指示も得られます。

あなたの街の例はおかしいと思います。都市名をハードコーディングすることは決してないでしょう。あなたがしていることに関係なく、後で追加の都市が含まれることは明らかです。しかし、別の例は色です。状況によっては、「赤」または「緑」をハードコーディングする可能性があります。たとえば、信号機はどこにでもある色なので、すぐに逃げることができます(いつでもリファクタリングできます)。違いは、「赤」と「緑」は、私たちの世界では普遍的な「ハードコード化された」意味を持ち、それが変わることは信じられないほどあり、実際には代替手段もありません。

最初の夏時間調整方法は単純に破られています。仕様に準拠していますが、ハードコードされた2018は特に悪いです。a)技術的な「契約」に記載されていないため(この場合、メソッド名に)、b)すぐに古くなるため、破損始めから含まれています。時刻/日付に関連するものについては、時間が進むため、特定の値をハードコーディングすることはほとんど意味がありません。しかし、それとは別に、他のすべては議論の余地があります。単純な年を指定し、常に完全な年を計算する場合は、先に進みます。リストしたもののほとんど(書式設定、より小さい範囲の選択など)は、メソッドの処理が多すぎることを叫び、代わりにおそらく呼び出し元が書式設定/フィルタリングを実行できるように値のリスト/配列を返す必要があります。

しかし、結局のところ、これのほとんどは意見、味、経験、個人的な偏見であるので、それについてあまり心配しないでください。


最後から2番目の段落について-最初に与えられた「要件」、つまり最初の方法のベースとなったものを見てください。2018を指定しているため、コードは技術的に正しい(そして、おそらく機能駆動型のアプローチと一致するでしょう)。
-dwizum

@dwizum、要件に関しては正しいですが、メソッド名が誤解を招く可能性があります。2019年、メソッド名を見ているプログラマーは、2018年ではなく、何でも(現在の年の値を返す可能性があります)を実行していると想定します...答えに文を追加して、意味を明確にします。
-AnoE

1

私は、2種類の再利用可能なコードがあると思うようになりました。

  • これは基本的で基本的なものなので、再利用可能なコードです。
  • どこにでもパラメーター、オーバーライド、およびフックがあるため、再利用可能なコード。

多くの場合、最初の種類の再利用性は良い考えです。リスト、ハッシュマップ、キー/値ストア、文字列マッチャー(regex、globなど)、タプル、統合、検索ツリー(深さ優先、幅優先、反復深化、...)などに適用されます。 、パーサーコンビネーター、キャッシュ/メモライザー、データ形式リーダー/ライター(s-expressions、XML、JSON、protobufなど)、タスクキューなど

これらは非常に抽象的な方法で非常に一般的であるため、日常のプログラミングのあちこちで再利用されています。より抽象的な/一般的なものにすればより簡単になる特別な目的のコードを書くことに気づいた場合(たとえば、「顧客注文のリスト」がある場合、「顧客注文」のものを捨てて「リスト」を取得できます) )それを引き出すことは良い考えかもしれません。再利用しなくても、無関係な機能を分離できます。

2番目の種類は、実際の問題を解決する具体的なコードがありますが、それは多くの決定を下すことによって行います。これらの決定を「ソフトコーディング」することで、より一般的/再利用可能にすることができます。たとえば、それらをパラメータに変換し、実装を複雑にし、さらに具体的な詳細をベイクします(オーバーライドに必要なフックの知識)。あなたの例はこの種のようです。この種の再利用性の問題は、他の人のユースケースや将来の自分を推測しようとする可能性があることです。最終的には、コードが使用できないほど多くのパラメーターを持つことになります、もちろん再利用可能!言い換えれば、それを呼び出すとき、私たち自身のバージョンを書くよりも多くの努力が必要です。これは、YAGNI(あなたはそれを必要としません)が重要な場所です。多くの場合、このような「再利用可能な」コードの試行は再利用されません。これは、パラメーターが説明できるよりも基本的にそれらのユースケースと互換性がない場合や、潜在的なユーザーが独自にロールバックする可能性があるためです(たとえば、先行者と区別するために、著者が「Simple」という語を接頭辞とする標準やライブラリがあります!)。

「再利用性」のこの2番目の形式は、基本的に必要に応じて実行する必要があります。確かに、いくつかの「明白な」パラメーターをそこに入れることができますが、将来を予測しようとしないでください。ヤグニ。


私の最初のテイクは、その年でさえハードコーディングされていたので、あなたは同意したと言えますか?または、最初にその要件を実装していた場合、その年を最初のテイクのパラメータにしますか?
コライトゥゲイ

要件は「何かをチェックする」ための1回限りのスクリプトであったため、最初のテイクは問題ありませんでした。彼女の「倫理」テストは失敗しますが、「ドグマなし」テストは失敗します。「彼女は言うかもしれない...」はあなたが必要としないだろう要件を発明しています。
ウォーボ

これ以上の情報がなければ、「破壊都市」が「より良い」とは言えません。これdestroyBaghdadは1回限りのスクリプトです(少なくとも、べき等です)。街を破壊することで改善されるかもしれませdestroyBaghdadが、チグリスに洪水を起こしてうまくいったらどうでしょうか?これは、モスルとバスラでは再利用可能ですが、メッカやアトランタでは再利用できません。
ウォーボ

引用の所有者であるナサニエル・ボレンスタインに同意しないようです。私はこれらの応答と議論をすべて読むことでゆっくり理解しようとしています。
コライトゥゲイ

1
私はこの差別化が好きです。常に明確であるとは限らず、常に「境界ケース」が存在します。しかし、一般的に、私static純粋に機能的で低レベルの「ビルディングブロック」(多くの場合、メソッドの形式)のファンでもあります。正当化を要求する構造を構築する必要があります。
Marco13

1

すでに多くの優れた精巧な答えがあります。それらのいくつかは特定の詳細に深く入り、一般的なソフトウェア開発方法論に関する特定の視点をレイアウトし、それらのいくつかは確かに論争の的となる要素または「意見」がまき散らされています。

Warbo答えは、すでにさまざまなタイプの再利用性を指摘しています。つまり、何かが基本的な構成要素であるために再利用可能かどうか、または何らかの方法で「汎用」であるために何かが再利用可能かどうかです。後者を参照すると、再利用性の何らかの手段として考えたいことがあります:

あるメソッドが別のメソッドをエミュレートできるかどうか。

質問の例について:メソッドが

void dayLightSavings()

顧客から要求された機能の実装でした。だから、何かになります他のプログラマが使うことになっているが、これにより、あること、公共のように、方法

publicvoid dayLightSavings()

これは、回答で示したように実装できます。今、誰かがそれを年でパラメータ化したいと考えています。メソッドを追加できます

publicvoid dayLightSavings(int year)

元の実装を変更して

public void dayLightSavings() {
    dayLightSavings(2018);
}

次の「機能要求」と一般化は同じパターンに従います。したがって、最も一般的な形式の需要がある場合のみ、それを実装できます。この最も一般的な形式では、より具体的な形式の簡単な実装が可能になることを知っています。

public void dayLightSavings() {
    dayLightSavings(2018, 0, 12, 0, 12, new DateTimeFormatter(...));
}

将来の拡張機能や機能のリクエストを予想していて、自由に使える時間があり、(潜在的に役に立たない)一般化で退屈な週末を過ごしたいなら、最初から最も一般的なもので始めたかもしれません。ただし、プライベートメソッドとしてのみ。顧客がパブリックメソッドとして要求した単純なメソッドのみを公開していれば、安全です。

tl; dr

問題は、実際には「メソッドがどれだけ再利用可能であるべきか」ではありません。問題は、この再利用性がどれだけ公開されているか、そしてAPIがどのように見えるかです。時の試練に耐えることができる信頼できるAPIを作成することは(後でさらに要件が出てきた場合でも)芸術であり技術であり、トピックはここでそれをカバーするにはあまりにも複雑です。見ていジョシュア・ブロックすることで、このプレゼンテーションAPIデザインブックウィキ開始のために。


dayLightSavings()電話をかけることdayLightSavings(2018)は私にとっては良い考えではないようです。
コライトゥゲイ

@KorayTugay最初の要求が「2018年の夏時間」を印刷することである場合、それは問題ありません。実際、これ元々実装した方法まったく同じです。それがために、「夏時間を印刷する必要がある場合は、現在の年、そしてもちろん、あなたが呼びたいdayLightSavings(computeCurrentYear());...。
Marco13

0

良い経験則は次のとおりです。メソッドは、再利用可能と同じくらい再利用可能でなければなりません。

1か所でのみメソッドを呼び出すことが予想される場合は、呼び出しサイトで認識され、このメソッドで使用できないパラメーターのみが含まれている必要があります。

より多くの呼び出し元がある場合、他の呼び出し元がそれらのパラメーターを渡すことができる限り、新しいパラメーターを導入できます。それ以外の場合は、新しいメソッドが必要です。

呼び出し元の数が時間とともに増加する可能性があるため、リファクタリングまたはオーバーロードに備える必要があります。多くの場合、式を選択し、IDEの「パラメータを抽出」アクションを実行しても安全だと感じるはずです。


0

非常に短い答え:汎用モジュールの他のコードへの結合または依存性が少ないほど、再利用可能性が高くなります。

あなたの例は依存するだけです

import java.time.*;
import java.util.Set;

そのため、理論的には非常に再利用可能です。

実際には、このコードを必要とする2つ目のユースケースがあるとは思わないので、このコードを必要とする3つ以上の異なるプロジェクトがない場合、yagniの原則に従って再利用できません。

再利用性の他の側面は、テスト駆動開発と相互作用する使いやすさと文書化です:汎用モジュールの簡単な使用をライブラリのユーザー向けのコーディング例として実証/文書化する単純な単体テストがある場合に役立ちます。


0

これは、私が最近作成したルールを述べる良い機会です。

優れたプログラマーであるということは、将来を予測できることを意味します。

もちろん、これは完全に不可能です!結局のところ、後で役立つ一般化、実行したい関連タスク、ユーザーが必要とする新機能などは確実にはわかりません。しかし、経験から、何が役に立つかについて大まかなアイデアを得ることがあります。

それに対してバランスを取る必要がある他の要因は、どれだけ余分な時間と労力がかかるか、そしてコードがどれだけ複雑になるかです。運がよければ、より一般的な問題の解決は実際には簡単です!(少なくとも概念的には、コードの量がそうでなければ。)しかし、より頻繁には、複雑なコストと時間と労力の1つがあります。

したがって、一般化が必要になる可能性が非常に高いと思う場合、多くの作業や複雑さを追加しない限り、それを行う価値があることがよくあります。可能性がはるかに低いと思われる場合は、おそらくそうではありません(非常に簡単であるか、コードを単純化していない限り)。

(最近の例:先週、システムは何かが期限切れになってからちょうど2日後に行うべきアクションの仕様を与えられました。したがって、もちろん2日間をパラメーターにしました。今週、ビジネス関係者は喜んでいます。私は幸運だった:それは簡単な変更であり、それは望まれる可能性が非常に高いと推測した。多くの場合、それは判断するのが難しい。 )


0

まず、「メソッドがどのように再利用可能であるかを知る方法」に対する最良の答えは「経験」です。これを数千回行うと、通常は正しい答えが得られます。この答えのライン:顧客は、どれだけの柔軟性といくつの一般化の層を求めるべきかを教えてくれます。

これらの回答の多くには、特定のアドバイスがあります。もっと一般的なものを与えたかったのです...皮肉なことはあまりにも楽しいからです!

いくつかの答えが指摘しているように、一般性は高価です。しかし、そうではありません。常にではない。再利用性ゲームをプレイするには、費用を理解することが不可欠です。

私は、物事を「不可逆」から「可逆」にスケールすることに焦点を当てています。滑らかなスケールです。本当に不可逆的なことは、「プロジェクトに費やした時間」だけです。これらのリソースを取り戻すことはありません。Windows APIなどの「黄金の手錠」の状況は、わずかに可逆性が低い場合があります。非推奨の機能は、Microsoftのビジネスモデルで求められているため、このAPIに何十年も残ります。何らかのAPI機能を取り消すと関係が永久に損なわれる顧客がいる場合、それは元に戻せないものとして扱う必要があります。スケールのもう一方の端を見ると、プロトタイプコードのようなものがあります。どこに行くか気に入らない場合は、単に捨てることができます。内部使用API​​の方がわずかに可逆的かもしれません。顧客を煩わせることなくリファクタリングできますが、時間(すべての最も不可逆的なリソース!)

したがって、これらを大規模に配置します。これで、ヒューリスティックを適用できます。可逆性が高いほど、将来のアクティビティに使用できるようになります。不可逆的なものがある場合は、具体的な顧客主導のタスクにのみ使用してください。これが、顧客が求めるものだけを実行することを提案する極端なプログラミングの原則のような原則を見る理由です。これらの原則は、後悔するようなことをしないようにするのに適しています。

DRY原則のようなものは、そのバランスを動かす方法を示唆しています。自分自身を繰り返していることに気付いた場合、それは基本的に内部APIを作成する機会です。顧客はそれを見ないので、いつでも変更できます。この内部APIを取得したら、将来を見据えたもので遊ぶことができます。あなたの妻があなたに与えると思うタイムゾーンベースのタスクはいくつありますか?タイムゾーンベースのタスクを必要とする可能性のある他の顧客がいますか? ここでの柔軟性は、現在の顧客の具体的な要求によって購入され、将来の顧客の潜在的な将来の要求をサポートします。

DRYから自然に生まれるこの階層化された思考アプローチは、無駄なく必要な一般化を自然に提供します。しかし、制限はありますか?もちろんあります。しかし、それを見るためには、木の森を見る必要があります。

柔軟なレイヤーが多数ある場合、顧客に直面するレイヤーを直接制御できなくなることがよくあります。私はソフトウェアを手に入れました。そこでは、10層の柔軟性が組み込まれているために、彼らが望んでいたものを手に入れることができなかった理由を顧客に説明しました。私たちは隅に自分自身を書いた。私たちは、私たちが必要だと思ったすべての柔軟性を結びつけました。

したがって、この一般化/ DRYトリックを行うときは、常に顧客に注意を払ってください。あなたの妻は次に何を求めると思いますか?これらのニーズを満たす立場に身を置いていますか?あなたがコツを持っている場合、顧客はあなたの将来のニーズを効果的に伝えます。あなたがコツを持っていなければ、まあ、私たちのほとんどは推測に頼るだけです!(特に配偶者の場合!)一部の顧客は優れた柔軟性を望み、これらのレイヤーの柔軟性から直接利益を受けるため、これらすべてのレイヤーでの開発の追加コストを喜んで受け入れます。他の顧客は揺るぎない要件をかなり固定しており、開発はより直接的であることを好みます。 顧客は、どれだけの柔軟性といくつの一般化層を求めるべきかを教えてくれます。


これを10000回やった人がいるはずなのに、なぜ10000回やって他の人から学ぶことができるのに経験を積む必要があるのでしょうか?回答は個人ごとに異なるため、経験豊富な回答は私には当てはまりませんか?またYour customer will tell you how much flexibility and how many layers of generalization you should seek.、これは何の世界ですか?
コライトゥゲイ

@KorayTugayビジネスの世界です。顧客が何をすべきかを言っていない場合、あなたは十分に耳を傾けていません。もちろん、彼らはいつも言葉であなたに話すことはありませんが、他の方法であなたに話します。経験は、彼らのより微妙なメッセージを聞くのに役立ちます。まだスキルを持っていない場合は、それらの微妙な顧客のヒントを聞くスキルを持っている社内の誰かを見つけて、指示のためにそれらを提供します。たとえCEOであっても、マーケティングであっても、誰かがそのスキルを持っているでしょう。
コートアンモン

特定のケースでは、特定のソリューションを一緒にハックするのではなく、このタイムゾーンの問題の一般化されたバージョンをコーディングするのに忙しすぎたためにゴミを取り出すことに失敗した場合、顧客はどう感じますか?
コートアンモン

あなたは私の最初のアプローチが正しいものであり、最初のテイクで2018をハードコーディングするのではなく2018をハードコーディングしたことに同意しますか?(ちなみに、それは本当に私の顧客の話を聞いているわけではありません。ごみの例です。それはあなたの顧客を知っているということです。 2018年の変更。)お時間をいただきありがとうございます。
コライトゥゲイ

@KorayTugay追加の詳細を知らなくても、ハードコーディングが適切なアプローチだったと思います。あなたは、将来のDLSコードが必要になるかどうかを知る方法も、彼女が次にどんな要求を出すかも考えもしませんでした。そして、顧客があなたをテストしようとしている場合、彼らは得たものを手に入れます= D
Cort Ammon

0

倫理的に訓練されたソフトウェアエンジニアがDestroyBaghdadの手順を作成することに同意することはありません。代わりに、基本的な職業倫理では、バグダッドをパラメータとして与えることができるDestroyCityプロシージャを記述することを求めます。

これは、高度なソフトウェアエンジニアリングサークルで「ジョーク」として知られているものです。ジョークは、私たちが「真実」と呼ぶものである必要はありませんが、おもしろくするためには、一般的に真実を示唆する必要があります。

この特定のケースでは、「ジョーク」は「true」ではありません。破壊するために一般的な手順を書くに関わる仕事すべての都市があり、我々は安全に1つの破壊するために必要なものを超えて、桁違いを想定することができ、特定のシティ。それ以外の場合、1つまたは少数の都市(聖書のジョシュア、またはトルーマン大統領)を破壊した人は誰でも、やったことを自明に一般化し、任意の都市を完全に破壊することができます。実際のところ、これは事実ではありません。これらの2人が有名な少数の特定の都市を破壊するために使用した方法は、いつでもどの都市でも機能するとは限りません。壁の共鳴周波数が異なる別の都市、または高高度の防空の方が優れている別の都市では、アプローチのマイナーまたは基本的な変更(ピッチの異なるトランペットまたはロケット)が必要です。

また、これは時間の経過に伴う変更に対するコードのメンテナンスにもつながります。現在の建設方法とユビキタスレーダーのおかげで、どちらのアプローチにも当てはまらない都市がかなり多くなっています。

1つの都市だけを破壊することに同意する前に、すべての都市を破壊する完全に一般的な手段の開発とテストは、非常に非効率的なアプローチです。倫理的に訓練されたソフトウェアエンジニアは、実証された要件なしに、雇用主/クライアントが実際に支払う必要があるよりも桁違いに多くの作業を必要とする程度に問題を一般化しようとしません。

それでは本当ですか?時々、一般性を追加するのは簡単です。そうするのが簡単な場合、常に一般性を追加する必要がありますか?長期的なメンテナンスの問題のために、私はまだ「常にではない」と主張します。執筆時点では、すべての都市は基本的に同じであると想定しているため、DestroyCityを使用します。これを作成したら、(有限の列挙可能な入力スペースによる)統合テストとともに、既知のすべての都市を反復処理し、それぞれで機能が動作することを確認します(動作方法は不明です。おそらくCity.clone()を呼び出します。クローンを破壊しますか?とにかく)。

実際には、この機能はバグダッドを破壊するためにのみ使用されます。誰かが私の技術に耐える新しい都市を建設すると仮定します(それは地下深くか何かです)。今では、実際には存在しないユースケースの統合テストに失敗しました。イラクの無実の民間人に対するテロのキャンペーンを続ける前に、Subterraniaを破壊する方法を理解する必要があります。これが倫理的であるかどうかを気にしないでください、それは愚かであり、私のおかしい時間の無駄です

では、2018年のデータを出力するだけで、どの年で夏時間を出力できる機能が本当に必要ですか?たぶん、しかし、それは確かにテストケースをまとめるだけで少し余分な努力が必要になるでしょう。実際のデータベースよりも優れたタイムゾーンデータベースを取得するには、多大な労力が必要になる場合があります。そのため、たとえば1908年、オンタリオ州ポートアーサーの町では、7月1日にDSTの期間が始まりました。あなたのOSのタイムゾーンデータベースにありますか?そうではないので、一般化された関数は間違っています。守れない約束をするコードを書くことに関して、特に倫理的なことは何もありません。

申し分なく、適切な注意を払えば、1970年から現在までの一定の時間帯でタイムゾーンを実行する関数を簡単に作成できます。しかし、実際に書いた関数を取得し、それを一般化して年をパラメータ化するのも同じくらい簡単です。ですから、今や一般化することは、もはや倫理的/賢明ではありません。あなたがしたことをし、必要なときに必要なときに一般化することです。

ただし、妻がこのDSTリストを確認する理由を知っていれば、2019年に再び同じ質問をする可能性が高いかどうか、またそうであれば、与えることでそのループから抜け出すことができるかどうかについて、情報に基づいた意見を得ることができます再コンパイルすることなく、呼び出すことができる関数。その分析を行ったら、「これを近年一般化すべきか」という質問に対する答えは「はい」になるかもしれません。しかし、あなた自身のためにさらに別の問題を作成します。これは、将来のタイムゾーンデータが暫定的なものであるため、彼女が今日 2019年に実行する場合です。、彼女はそれが彼女に最高の推測を与えていることを理解するかもしれないし、しないかもしれない。そのため、あまり一般的でない機能には必要のないドキュメントをたくさん書く必要があります(「データはタイムゾーンデータベースから得られます。特別なケースを拒否すると、その間ずっと、彼女は2018年のデータが必要だったタスクに取り組むことができません。

冗談で言われたからといって、きちんと考えずに難しいことをしないでください。便利ですか?その程度の有用性に十分なほど安いですか?


0

アーキテクチャの宇宙飛行士によって定義されている再利用性はブロックであるため、これは簡単に描くことができます。

アプリケーション開発者が作成するほとんどすべてのコードは、非常にドメイン固有です。これは、わざわざ1980年のほぼすべての価値ではありませんすでに枠組みの中で。

抽象化と規則には、文書化と学習努力が必要です。そのためだけに新しいものを作成しないでください。(私はあなたを見ています、JavaScriptの人々!)

あなたが選択したフレームワークに本当にあるべきものを見つけたという信じがたい幻想にふけってみましょう。通常の方法でコードを強打することはできません。おお、いや、使用目的だけでなく、使用目的からの逸脱、既知のすべてのエッジケース、考えられるすべての障害モード、診断用テストケース、テストデータ、技術文書、ユーザードキュメント、リリース管理、サポートのためにテストカバレッジが必要ですスクリプト、回帰テスト、変更管理...

あなたの雇用主はすべてのことを喜んで支払いますか?私はノーと言うつもりです。

抽象化は、柔軟性のために支払う価格です。コードが複雑になり、理解しにくくなります。柔軟性が現実の現在のニーズにかなう場合を除き、YAGNIを使用してください。

対処しなければならなかった現実の例を見てみましょう:HTMLRenderer。プリンターのデバイスコンテキストにレンダリングしようとしたときに、適切にスケーリングされませんでした。発見する前に2つのアセンブリで6レベルの間接参照を歩き回らなければならなかったため、デフォルトではGDI +(ではなくアンチエイリアスではない)ではなくGDI(スケーリングしない)を使用していることを発見するのに一日中かかりました何でもしたコード。

この場合、著者を許します。これ、WinForms、WPF、dotnet Core、Mono、およびPdfSharpという5つの非常に異なるレンダリングターゲットを対象とするフレームワークコードであるため、抽象化が実際に必要です。

しかし、それは私のポイントを強調するだけです:すべてのプラットフォームで高いパフォーマンスを実現するという目標を掲げて、複数のプラットフォームをターゲットとする非常に複雑な(スタイルシートを使用したHTMLレンダリング)ことはほぼ間違いありません

あなたのコードは、ほぼ間違いなく、雇用主にのみ適用されるビジネスルールと、販売されていないアプリケーションで、あなたの州にのみ適用される税ルールを持つ、別のデータベースグリッドです。

すべてのこと間接の問題、あなたが持っていないを解決し、あなたのコードを作るくらいの大規模メンテナンスのコストを増加し、あなたの雇用に大きな害である、読みにくいです。幸いなことに、これについて文句を言うべき人々は、あなたが彼らに対してしていることを理解することができません。

反論としては、この種の抽象化はテスト駆動開発をサポートしますが、TDDは、ビジネスがその要件を明確かつ完全かつ正確に理解していることを前提としているため、TDDもまとまりがあると思います。TDDは、NASAおよび医療機器と自動運転車の制御ソフトウェアには最適ですが、他のすべての人にはあまりにも高価です。


ちなみに、世界の夏時間をすべて予測することはできません。特にイスラエルには、毎年約40の移行があります。これは、人々が間違った時間に祈ることができず、神が夏時間を行わないためです。


抽象化と学習努力について、特に「JavaScript」と呼ばれる憎悪を目的とする場合、あなたが言ったことには同意しますが、最初の文には強く反対しなければなりません。再利用性は多くのレベルで発生する可能性がありますが、それはあまりにも下に行く可能性があり、スローアウェイコードはスローアウェイプログラマーによって書くことができます。しかし、そこにある少なくともに十分な理想主義的な人目的再利用可能なコードでは。あなたがこれが持つことができる利点を見ないならば、あなたのために残念です。
Marco13

@ Marco13-あなたの反対は合理的であるため、私は要点を拡大します。
ピーターウォン

-3

少なくともJava 8を使用している場合は、WorldTimeZonesクラスを記述して、本質的にタイムゾーンのコレクションのように見えるものを提供します。

次に、WorldTimeZonesクラスにfilter(Predicate filter)メソッドを追加します。これにより、パラメーターとしてラムダ式を渡すことで、呼び出し元が必要なものをフィルター処理できるようになります。

本質的に、単一のフィルターメソッドは、述語に渡される値に含まれるすべてのフィルターをサポートします。

または、呼び出されたときにタイムゾーンのストリームを生成するWorldTimeZonesクラスにstream()メソッドを追加します。その後、呼び出し元は、特殊化をまったく書かなくても、必要に応じてフィルター、マップ、および縮小を行うことができます。


3
これらは優れた一般化のアイデアですが、この答えは質問で何が求められているかについてのマークを完全に逃しています。問題は、ソリューションをどのように最適化するかではなく、一般化でどこに線を引くか、これらの考慮事項を倫理的にどのように評価するかです。
maple_shaft

したがって、作成の複雑さによって変更されたユースケースの数でそれらを比較検討する必要があると言っています。1つのユースケースをサポートするメソッドは、20のユースケースをサポートするメソッドほど価値がありません。一方、知っているのが1つのユースケースだけであり、コードを作成するのに5分かかる場合は、そのままにしてください。多くの場合、特定の用途をサポートするコーディング方法は、一般化する方法を知らせます。
ロドニーP.バルバティ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.