web-dev-qa-db-ja.com

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

私は家で自分のビジネスを気にしていて、妻がやって来て言います

Honey ..コンソールで2018年の世界中のすべてのデイライトセービングを印刷できますか?何か確認する必要があります。

そして、それが私のJavaの経験と思いついて、私の人生を通してずっと待っていたものでしたので、とても幸せです:

_import Java.time.*;
import Java.util.Set;

class App {
    void dayLightSavings() {
        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プロシージャを作成する必要があります。

そして、私は大丈夫です、大丈夫、大丈夫、あなたは私を手に入れました。あなたが好きな任意のを渡します、ここに行きます:

_import Java.time.*;
import Java.util.Set;

class App {
    void dayLightSavings(int year) {
        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.DESTROY_、次に_Action.REBUILD_で呼び出すことができますね。

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

_takeActionOnCity(Action.DESTORY, City.BAGHDAD);
_

その後

_takeActionOnCity(Action.DESTORY, City.ERBIL);
_

そして私ができるときなど:

_takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);
_

pS私は、私が言及した引用を中心に私の質問を作成しました。私は、世界の国、宗教、人種などには何も反対していません。私はただ主張をしているだけです。

136
Koray Tugay

カメはずっと下にいます。

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

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

たとえば、単純なアプリケーションを最初に要求し、次に拡張を要求することが知られているお客様がいました。また、お客様は何が欲しいのかを尋ねるお客様もいましたが、通常、拡張のために私たちに戻ってくることはありません。
お客様のアプローチはお客様ごとに異なります。最初の顧客の場合、コードを先制的に抽象化して(---)将来的にこのコードに再度アクセスする必要があると確信しているため、支払います。 2番目の顧客の場合、アプリケーションを拡張したくないと思われる場合は、余分な労力を費やしたくない場合があります(注:これは、適切な方法に従わないことを意味するのではなく、単に現在必要な以上のことは避けてください

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

上記の理由は、すでにこの罠に陥っているからです。

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

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

ただし、ここではコンテキストが適用されます。私はあなたの妻を知りません。多分あなたは彼女が実際にこれを望んでいることを正確に判断しました。しかし、それでも顧客に確認するこれは確かに顧客が望んでいることです。そうしないと、使用することのない機能の開発に時間を費やすことになります。

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

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

ただし、お客様に正しい質問をするを行うことで、その必要性を確認できます。アーキテクチャについて尋ねるのではなく、コードベースの将来の開発または拡張に対する期待について尋ねます。また、必要な時間枠でファンシーアーキテクチャを実装できない可能性があるため、現在の目標に期限があるかどうかを尋ねることもできます。

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

私はそれをどこで読んだかわかりません(誰かが知っている場合は、私に知らせて、クレジットを差し上げます)が、経験則として、開発者は穴居人のように数える必要があります:one、two many =。

enter image description hereXKCD#764

言い換えると、特定のアルゴリズム/パターンがthirdの時間使用されている場合、再利用できるように抽象化する必要があります(=使用可能多く回)。

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

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

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

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

  • any都市で実行できる複数のアクションはありますか?
  • これらのアクションは互換的に使用できますか? 「破棄」アクションと「再構築」アクションの実行がまったく異なる場合、2つのアクションを1つのtakeActionOnCityメソッドにマージしても意味がありません。

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

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

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

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

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

116
Flater

練習

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

今はあまり役に立ちませんでは、いくつかのガイドラインについてはどうですか?

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

ガイドライン1: あなたはそれを必要としないでしょう

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

ガイドライン2: Cost/Benefit

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

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

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

44
Telastyn

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

私も、別の文脈に適用できなくても、知恵が明らかな命令として何でも書くことができます。文字列連結を承認する必要があるのはなぜですか?

次に、実行時のすべてのコードを完全に指定する必要があります

決定がハードコーディングされているか、別のレイヤーに委ねられているかは関係ありません。ある時点で、何が破壊され、どのようにそれを指示するかを知っているいくつかの言語のコードがあります。

これは、同じオブジェクトファイルdestroyCity(xyz)にある場合と、構成ファイルdestroy {"city": "XYZ"}"にある場合と、UIでの一連のクリックとキープレスの場合があります。

第三に:

Honey ..コンソールで2018年の世界中のすべてのデイライトセービングを印刷できますか?何か確認する必要があります。

は、次の要件が大きく異なります。

彼女はカスタム文字列フォーマッタを渡そうとしています...特定の月の期間のみに関心があります... ...特定の時間の期間に関心があります...

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

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

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

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

最終的に。 あなたの時間は重要です;それは非常に現実的で非常に有限です。あなたが行うすべての決定は、他の多くの隠された決定を伴います、そしてこれがソフトウェアの開発についてです。決定を引数として遅らせると、現在の関数が単純になる可能性がありますが、他の場所ではより複雑になります。その決定はその別の場所に関連していますか?ここでもっと関連性がありますか?それは本当に誰の決定ですか?あなたはこれを決定しています。これはコーディングです。一連の決定を頻繁に繰り返す場合、それらをいくつかの抽象化の中で体系化することには非常に大きなメリットがあります。 [〜#〜] xkcd [〜#〜] は、ここで有用な視点を持っています。そして、これは、関数、モジュール、プログラムなど、システムのレベルで関係があります。

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

27
Kain0_0

ここには長々とした答えがたくさんありますが、正直なところ、それは非常に簡単だと思います

関数名の一部ではない、関数内にあるハードコードされた情報は、パラメーターにする必要があります。

だからあなたの機能で

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インスタンスに紹介する必要があります。バックログに入れて、適切なプロジェクト管理委員会の会議で優先順位を付けることができます。

4
Ewan

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

提供した例のように単純なものの場合、初期の実装は非常に小さいため問題ありませんが、(設計の柔軟性ではなく)機能の柔軟性を過度に詰め込もうとすると、この単純な設計は理解しにくくなり、もろくなります。 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の内部の仕組みに関する詳細な知識Cityの動作を変更する必要が生じた場合にそれらの外部要素を考慮する必要があるため、それ自体は緊密な結合となり、柔軟性を低下させます。これがカプセル化の真の目的です。すべてのオブジェクトには独自のAPIがあり、必要なことをすべて実行できるようになっているので、リクエストの処理について心配させることができると考えてください。

「コントロール」ではなく委任

ここで、実装クラスがCityであるかBaghdadであるかは、都市を破壊するプロセスがどの程度一般的であるかによって異なります。おそらく、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ライブラリのクラスを使用していますが、この設計をサポートしていません。このため、ZonedDateTimeのAPIを直接使用できます。ここでの考え方は、各計算が独自のオブジェクト内にカプセル化されるということです。実行回数や結果のフォーマット方法については想定していません。最も単純な形式の計算の実行のみに関係しています。これにより、両方が簡単になります同様に、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()
            );
        });
    }
}
_

実際のところ、オブジェクト指向プログラミングは、命令型パラダイムの複雑性/柔軟性の問題の解決策として具体的に発明されました。イディオム内で命令型の関数と手続きを指定します。

4
Stuporman

経験ドメイン知識コードレビュー。

また、experiencedomain knowledge、またはteamの数に関係なく、必要に応じてリファクタリングします。


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

(このエクスペリエンスは、一部のドメインオブジェクトおよびメソッドにも直感的に転送される場合があります。)

ドメインナレッジを使用すると、どのビジネスコンセプトが密接に関連しているか、どのコンセプトに変数があるか、かなり静的であるかなどがわかります。

コードレビューを使用すると、ピアが(うまくいけば)ユニークなエクスペリエンスとパースペクティブを両方とも持つので、プロダクションコードになる前に、パラメータ化の過不足が捕捉される可能性が高くなります- ドメインおよびコーディング一般。


とはいえ、新しい開発者は通常、これらのSpidey Sensesや経験豊富な仲間のグループがすぐに頼りになることはありません。そして経験豊富でも、開発者は基本的な規律から新しい要件や頭の悪い日々に導いてくれます。だから、これが私が提案するものです最初に

  • 最小限のパラメーター化で、単純な実装から始めます。
    (あなたがすでに持っているパラメータを含めてくださいknow必要になるでしょう、明らかに...)
  • マジックナンバーと文字列を削除し、それらを設定やパラメータに移動します
  • 「大きな」メソッドをより小さな名前の付いたメソッドに分解する
  • 非常に冗長なメソッド(便利な場合)を共通の分母にリファクタリングして、違いをパラメータ化します。

これらの手順は、必ずしもこの順序で行われるとは限りません。 既存のメソッドで非常に冗長であることがすでにわかっているの場合は、すぐにリファクタリングに進んでくださいそれが便利な場合(大幅に増加しない場合) 2つのメソッドを記述、テスト、保守するだけの場合よりも、リファクタリングにより多くの時間がかかります。

しかし、単に多くの経験などがあることは別にして、私はかなり最小限のコードをDRYすることをお勧めします。明らかな違反をリファクタリングすることは難しくありません。そして、あなたがtoo zealousである場合、 "WET"同等のものよりも読み取り、理解、維持がさらに困難な "over-DRY"コードになる可能性があります。

3
svidgen

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

あなた、ユーザーと同じくらい再利用可能、1 それらが必要です

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

  • 「一歩下がった」というフレーズに注意してください。ここにはペイオフメカニズムがあり、このコードをさらにどの程度処理するかによって異なります。例えば。:
    • これは1回限りのプロジェクトですか、それとも長期にわたって徐々に改善される予定ですか?
    • デザインに自信がありますか、それとも次のプロジェクト/マイルストーン(たとえば、別のフレームワークを試す)のために廃棄するか、大幅に変更する必要がありますか?
  • 予測される利益は、将来を予測する能力(アプリの変更)にも依存します。時々、あなたはあなたのアプリがとろうとしている会場を合理的に見ることができます。多くの場合、あなたはできると思っていますが、実際にはできません。ここでの経験則は YAGNIの原則の法則 -ですが、どちらも今は知っていることからの作業を強調しています。

1これはコード構造なので、この場合は「ユーザー」、つまりソースコードのユーザーになります。

2
ivan_pozdeev

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

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

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

これらはso一般的なものであり、非常に抽象的な意味で、日常のプログラミングのあらゆる場所で再利用されています。 simplerのような特別な目的のコードを記述していることに気付いた場合、より抽象的/一般的にした場合(たとえば、「顧客注文のリスト」がある場合)、「顧客注文」を破棄できます。 「リスト」を取得するためのもの)それからそれを引き出すのは良い考えかもしれません。再利用されなくても、無関係な機能を切り離すことができます。

2番目の種類は、実際の問題を解決する具体的なコードがある場所ですが、一連の決定を行うことで実現します。これらの決定を「ソフトコーディング」することで、より一般的/再利用可能にすることができます。それらをパラメータに変換しますcomplicating実装とさらに具体的な詳細(つまり、オーバーライドに必要なフックの知識)を焼きます。あなたの例はこのようなもののようです。この種の再利用性の問題は、他の人のユースケースや将来の自分自身を推測しようとする可能性があることです。最終的に、コードがsableでないほど多くのパラメーターを持つことになるかもしれません。言い換えれば、呼び出すときは、私たち自身のバージョンを書くよりも多くの労力が必要です。ここでYAGNI(You Ai n't Gonna Need It)が重要です。多くの場合、そのような「再利用可能な」コードでの試行は再利用されなくなります。これは、パラメータが説明できるよりも根本的にこれらのユースケースと互換性がない場合や、潜在的なユーザーが自分でロールする場合があるためです(すべて、以前のバージョンと区別するために、「シンプル」という単語をプレフィックスとして作成者が付けた標準とライブラリ!).

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

1
Warbo

すでに多くの優れた手の込んだ答えがあります。それらのいくつかは、特定の詳細に深く入り、一般にソフトウェア開発方法論に関する特定の視点を示します、そしてそれらのいくつかは確かに物議を醸す要素または「意見」がちりばめられています。

Warboによる回答 は、再利用性の異なるタイプをすでに指摘しています。つまり、何かが基本的なビルディングブロックであるために再利用可能であるかどうか、または何らかの方法で「ジェネリック」であるために再利用可能であるかどうかです。後者を参照すると、再利用性のためのある種のmeasureと私が考えるものがあります:

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

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

_void dayLightSavings()
_

顧客から要求された機能の実装でした。したがって、他のプログラマが使用することになっているものであり、したがって、publicメソッドになりますのように

publicvoid dayLightSavings()

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

publicvoid dayLightSavings(int year)

元の実装を変更して

_public void dayLightSavings() {
    dayLightSavings(2018);
}
_

次の「機能リクエスト」と一般化は同じパターンに従います。したがって、ifおよびifのみ、最も一般的な形式の需要があります。この最も一般的な形式では、より具体的な形式の簡単な実装が可能であることを理解して、実装できます。

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

将来の拡張や機能のリクエストを予想していて、時間に余裕があり、退屈な週末を(役に立たない可能性がある)一般化に費やしたい場合、できます最初から一般的なもの。しかし、onlyprivateメソッドとして使用します。お客様からリクエストされたsimpleメソッドのみをpublicメソッドとして公開した場合、あなたは安全です。

tl; dr

問題は、実際には「メソッドがどの程度再利用可能であるべきか」ということではありません。問題は、この再利用性のどの程度が公開されているか、そしてAPIがどのように見えるかです。時間のテストに耐えられる信頼性のあるAPIを作成することは(後でさらに要件が発生した場合でも)芸術であり、技術であり、トピックはここで説明するには複雑すぎます。まずは Joshua Blochによるこのプレゼンテーション または API設計書wiki をご覧ください。

1
Marco13

あなたが従うことができる明確なプロセスがあります:

  • それ自体が「もの」である単一の機能の失敗するテストを記述します(つまり、どちらも半分が実際に意味をなさない機能の任意の分割ではありません)。
  • 絶対最低限のコードを記述して、1行以上ではなく、環境に優しいようにします。
  • すすぎ、繰り返します。
  • (必要に応じて容赦なくリファクタリングします。テストカバレッジが優れているため、これは簡単です)

これは-少なくとも一部の人々の意見では-最適なコードであることがわかります。コードは可能な限り小さいため、完成した各機能は可能な限り少ない時間で完了します(完成したものを見ると、そうでない場合もあります)リファクタリング後の製品)、非常に優れたテストカバレッジがあります。また、過度に設計された、あまりに一般的なメソッドやクラスを著しく回避します。

これは、物事を一般化するときと専門化するときの非常に明確な指示も提供します。

あなたの街の例は奇妙だと思います。都市名をハードコードすることは決してないでしょう。あなたが何をしているとしても、後で追加の都市が含まれることは明らかです。しかし、別の例は色です。状況によっては、「赤」または「緑」をハードコーディングすることが考えられます。たとえば、信号機はどこにでもある色であり、それを使うだけで十分です(いつでもリファクタリングできます)。違いは、「赤」と「緑」が私たちの世界で普遍的な「ハードコードされた」意味を持っていること、それが変わることは信じられないほど信じられないこと、そして本当に代替案もないということです。

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

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

1
AnoE

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

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

もちろん、これは絶対に不可能です!結局のところ、sureについて後で一般化する一般化、実行する必要のある関連タスク、新機能ユーザーは&c。しかし、経験から、何が役に立つかを大まかに知ることができます。

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

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

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

0
gidds

最初に、「メソッドの再利用可能性をどのように知ることができますか?」に対する最良の答えは「経験」です。これを数千回実行すると、通常は正しい答えが得られます。しかし、ティーザーとして、私はこの回答の最後の行を示します。顧客は、どの程度の柔軟性と一般化の層をいくつ探すべきかを教えてくれます。

これらの回答の多くには、特定のアドバイスがあります。もっと一般的なものを付けたかったのです...

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

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

したがって、これらをスケールに入れます。これでヒューリスティックを適用できます。何かが可逆的であるほど、それを将来のアクティビティに使用できます。元に戻せないものがある場合は、顧客主導の具体的なタスクにのみ使用してください。これが、顧客が求めていることだけを実行し、それ以上は実行しないことを示唆する極端なプログラミングのような原則を見ている理由です。これらの原則は、後悔することをしないようにするのに優れています。

DRY原則のようなものは、そのバランスを移動する方法を提案します。自分自身を繰り返すことに気付いた場合、それは基本的に内部APIであるものを作成する機会です。顧客にはそれが表示されないため、いつでも変更できますこの内部APIを取得したら、今度は前向きなことを試してみることができます。妻が提供する予定のタイムゾーンベースのタスクはいくつありますか?タイムゾーンベースのタスクを必要とする可能性のある他の顧客はいますか?- ここでの柔軟性は、現在の顧客の具体的な要求によって得られ、将来の顧客の潜在的な将来の要求をサポートします。

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

柔軟性のあるレイヤーが多数ある場合、顧客が直面するレイヤーを直接制御できないことがよくあります。私はソフトウェアを手に入れましたが、10層に組み込まれている柔軟性が原因で、本来見るべきではなかった柔軟性が理由で顧客が欲しいものを手に入れることができない理由を顧客に説明するという残忍な仕事をしました。私たちは隅に自分で書いた。私たちは、私たちが必要と考えたすべての柔軟性を備えた結び目で結ばれました。

したがって、この一般化/ DRYトリックを実行しているときは、常に顧客にパルスを維持するです。あなたの妻は次に何を求めると思いますか?それらのニーズを満たす立場にいますか?コツがあれば、顧客は将来のニーズを効果的に教えてくれます。コツがない場合、まあ、私たちのほとんどは当て推量に依存しています! (特に配偶者の場合!)一部の顧客は、優れた柔軟性を望んでおり、これらの層の柔軟性のメリットを直接享受できるため、これらすべての層を使用して開発する場合の追加コストを受け入れることをいとわないでしょう。他の顧客は固定された不変の要件を持っており、彼らは開発がより直接的であることを望んでいます。 あなたの顧客は、どの程度の柔軟性と何層の汎化を追求すべきかを教えてくれます。

0
Cort Ammon

建築宇宙飛行士によって定義された再利用性は一瞬であるので、これは簡単に描ける線です。

アプリケーション開発者が作成するほとんどすべてのコードは極めてドメイン固有です。これは1980年ではありません。わずらわしい価値のあるほとんどすべてがフレームワークではalreadyです。

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

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

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

抽象化は、柔軟性のために支払う代償です。コードが複雑になり、理解が難しくなります。柔軟性が実際の現在のニーズを満たさない限り、YAGNIはそうではありません。

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

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

しかし、それは私の要点を強調するだけです:あなたはほぼ確実にnotすべてのプラットフォームで高いパフォーマンスの目標を掲げて複数のプラットフォームをターゲットとする非常に複雑な(スタイルシートによるHTMLレンダリング)をしています。

Yourコードは、ほぼ確実に、雇用者にのみ適用されるビジネスルールと、販売されていないアプリケーションでの州にのみ適用される税ルールを備えた別のデータベースグリッドです。

これらすべての間接参照は、あなたが持っていない問題を解決してコードを読みにくくしますmuchが読みにくくなり、メンテナンスのコストが大幅に増加し、雇用者にとって大きな負担となります。幸い、これについて不平を言うべき人々はあなたが彼らに何をしているのか理解することができません。

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


ちなみに、all世界の夏時間を予測することはできません。特にイスラエルでは、毎年約40のトランジションがあり、人々が間違った時間に祈ったり、神が夏時間を作ったりすることができないため、あちこちを飛び回っています。

0
Peter Wone

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

これは、高度なソフトウェアエンジニアリング業界では「冗談」として知られています。ジョークは、私たちが「本当」と呼ぶものである必要はありませんが、おもしろいには、本当のことをほのめかす必要があります。

この特定のケースでは、「冗談」は「真」ではありません。 any都市を破壊するための一般的な手順の記述に関連する作業は、1つの特定都市を破壊するのに必要な規模をはるかに超えると想定できます。それ以外の場合、1つまたはいくつかの都市(聖書のジョシュア、私たちが言おうとしている、またはトルーマン大統領)を破壊した人なら誰でも、自分のしたことを簡単に一般化し、任意の都市を完全に破壊することができます。実際、これは事実ではありません。それらの2人が少数の特定の都市を破壊するために有名に使用した方法は、いつでもどの都市でも必ずしも機能するとは限りません。壁の共振周波数が異なる、または高層防空がより優れている別の都市では、アプローチを少しまたは基本的に変更する必要があります(ピッチの異なるトランペットまたはロケット)。

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

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

それで本当は何ですか?時々、一般性を追加することは簡単です。次に、そうするのが簡単な場合は、常に一般性を追加する必要がありますか?長期的なメンテナンスの問題があるため、私は「いいえ、常にではない」と主張します。執筆の時点では、すべての都市は基本的に同じであると仮定すると、DestroyCityに進みます。これを書いたら、(入力の有限列挙可能なスペースに起因する)統合テストとともに、すべての既知の都市を反復処理し、関数が各都市で機能することを確認します(機能がどのように機能するかはわかりません。おそらくCity.clone()を呼び出し、クローンを破壊しますか?とにかく)。

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

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

さて、適切な警告があれば、1970年から現在までのように、さまざまな年のタイムゾーンを実行する関数を作成するのは簡単です。しかし、実際に記述した関数を取り、それを一般化して年をパラメーター化するのも同じくらい簡単です。したがって、今一般化することは、これ以上実際に倫理的/賢明なことではありません。つまり、あなたがしたことを実行し、必要に応じて一般化することです。

ただし、あなたが知っている場合なぜ妻がこのDSTリストを確認したい場合は、2019年に同じ質問をする可能性があるかどうか、またそうである場合は、再コンパイルする必要なしに、彼女が呼び出すことができる関数を彼女に与えることによって、ループから抜け出します。いったんその分析を終えたら、「これは近年に一般化されるべきか」という質問に対する答えは「はい」かもしれません。しかし、あなたはまだあなた自身を作成し​​ますanother問題を自分で作成します。これは、将来のタイムゾーンデータは暫定的なものであるため、2019年に実行するとtodayである場合とそうでない場合がありますそれが彼女に最良の推測を与えていることを理解してください。したがって、あまり一般的ではない機能には必要のない一連のドキュメントを作成する必要があります(「データはタイムゾーンデータベースから取得されます。これは、更新のプッシュに関するポリシーを確認するためのリンクです」です)。特別なケースを拒否した場合、その間ずっと、彼女は2018年のデータが必要なタスクを続行できません。これは、2019年についてはナンセンスであるため、まだ気にしていません。

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

0
Steve Jessop

原則として、メソッドは再利用可能である必要があります。

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

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

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

0
Karol

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

あなたの例は

import Java.time.*;
import Java.util.Set;

したがって、理論的には再利用性が高くなる可能性があります。

実際には、このコードを必要とする2番目のユースケースが発生することはないと思います。 yagni の原則に従うと、このコードを必要とする3つを超える異なるプロジェクトがない場合、再利用できなくなります。

再利用性の他の側面は、使いやすさおよび テスト駆動開発 と相互に関連する文書化です:コーディング例としての一般的なモジュールの簡単な使用を実証/文書化する単純な単体テストがある場合に役立ちますあなたのlibのユーザーのために。

0
k3b