web-dev-qa-db-ja.com

抽象化のレベルを決定する方法

私は本日 "Clean code" と呼ばれる本を読んでいて、著者が関数ごとの抽象化のレベルについて話している段落に出くわし、彼はいくつかのコードを低/中/高抽象化。

私の質問は、抽象化のレベルを決定するための基準は何ですか?

私は本からの段落を引用します:

関数が「1つのこと」を実行していることを確認するには、関数内のステートメントがすべて同じ抽象化レベルであることを確認する必要があります。リスト3-1がこの規則にどのように違反しているかは簡単にわかります。 getHtml()のように、非常に高度な抽象化の概念があります。次のような抽象化の中間レベルにある他のもの:String pagePathName = PathParser.render(pagePath);さらに、.append( "\ n")などの非常に低いレベルのその他のものもあります。

35
OKAN

著者は、抽象化について説明する部分(階層的なインデント鉱山)の「コードを上から下に読む」サブセクションで次のように説明しています。

[...]プログラムが[〜#〜] to [〜#〜]段落のセットであるかのようにプログラムを読み取ることができるようにしたい後続の[〜#〜] to [〜#〜]段落を抽象化して参照し、次のレベルの段落に移動します。

  • セットアップとティアダウンを含めるには、セットアップを含め、次にテストページのコンテンツを含め、ティアダウンを含めます。
    • セットアップを含めるには、これがスイートの場合はスイートのセットアップを含め、次に通常のセットアップを含めます。
      • スイートのセットアップを含めるには、「SuiteSetUp」ページの親階層を検索し、そのページのパスを含むincludeステートメントを追加します。
        • 親を検索するには...

これに伴うコードは次のようになります。

public void CreateTestPage()
{
    IncludeSetups();
    IncludeTestPageContent();
    IncludeTeardowns();
}

public void IncludeSetups()
{
    if(this.IsSuite())
    {
        IncludeSuiteSetup();
    }

    IncludeRegularSetup();
}

public void IncludeSuiteSetup()
{
    var parentPage = FindParentSuitePage();

    // add include statement with the path of the parentPage
}

等々。関数階層をさらに深く進むたびに、抽象化のレベルを変更する必要があります。上記の例では、IncludeSetupsIncludeTestPageContentIncludeTeardownsはすべて同じ抽象化レベルにあります。

本の例では、著者は、大きな機能を非常に具体的で1つのことだけを行う小さな機能に分割することを提案しています。正しく行うと、リファクタリングされた関数は、ここの例のようになります。 (リファクタリングされたバージョンは、本のリスト3-7に示されています。)

27
Adam Lear

この質問を理解するには、抽象化とは何かを理解する必要があります。 (私は正式な定義を見つけるのが面倒なので、きっとうんざりするつもりですが、ここに行きます...)抽象化とは、複雑な主題またはエンティティを取り、その詳細のほとんどを隠すことですそれでも、そのオブジェクトの本質を定義する機能を公開します。

本があなたに与えた例は家だったと思います。家を非常に詳細に見ると、板、釘、窓、ドアでできていることがわかります。しかし、写真の横にある家の漫画の絵は、それが欠けていても、まだ家です。それらの詳細の多く。

ソフトウェアでも同じことが言えます。本が忠告するように、プログラムするときはいつでも、あなたはソフトウェアをレイヤーとして考える必要があります。与えられたプログラムは、100以上のレイヤーを簡単に持つことができます。一番下には、CPUで実行されるアセンブリ命令があります。より高いレベルでは、これらの命令が組み合わされてディスクI/Oルーチンを形成する場合があります。さらに高いレベルでは、ディスクI /を操作する必要はありません。 Windows関数を使用してファイルを開く/読み取り/書き込み/検索/閉じるだけなので、直接O。これらはすべて、独自のアプリケーションコードに到達する前でも抽象化されています。

コード内では、抽象化レイヤーが続きます。下位レベルの文字列/ネットワーク/データ操作ルーチンがあるかもしれません。より高いレベルでは、これらのルーチンを、ユーザー管理、UIレイヤー、データベースアクセスを定義するサブシステムに組み合わせることができます。さらに別のレイヤーでは、これらのサブシステムを組み合わせてサーバーコンポーネントにまとめ、大規模なエンタープライズシステムの一部にすることができます。

これらの各抽象化レイヤーの鍵は、それぞれが前のレイヤーによって公開された詳細を隠し、次のレイヤーによって消費される非常にクリーンなインターフェースを提供することです。ファイルを開くために、個々のセクターの書き込み方法や処理するハードウェア割り込みを知っている必要はありません。しかし、抽象化レイヤーチェーンをたどり始めれば、Write()関数呼び出しから、ハードドライブコントローラーに送信された正確な命令まで、確実にトレースできます。

作者があなたに言っていることは、クラスまたは関数を定義するとき、自分がどの層であるかについて考えます。サブシステムとユーザーオブジェクトを管理しているクラスがある場合、同じクラスが低レベルの文字列操作を実行したり、ソケット呼び出しを行うためだけの変数の束全体を含むべきではありません。それは、抽象化レイヤーの交差、および1つのクラス/関数が1つのことだけを実行することの違反になります(SRP-単一責任の原則)。

10
DXM

私の質問は、抽象化のレベルを決定するための基準は何ですか?

抽象化レベルは明白であるはずです。問題ドメインの一部である場合は抽象的であり、プログラミング言語の一部ではありません。 「非常に抽象的な」==「実際ではない」==「問題のあるドメイン」よりも明確にするのは難しい。そして「抽象的ではない==具象==言語の一部」。抽象化レベルを決定するのは簡単なことです。微妙なことはまったくないはずです。

.append("\n")は抽象的ではありません。文字列に文字を置くだけです。それは具体的です。抽象的ではありません。

String pagePathName = PathParser.render(pagePath);は文字列を扱います。具体的なこと。一部は具体的なプログラミング言語機能にあります。 「パス」と「パーサー」の抽象的な概念を部分的に扱う。

getHtml();抽象。 「マークアップ」や、些細で具体的な言語機能ではないものを扱います。

抽象==言語機能ではありません。

具体的な==言語機能。

2
S.Lott

抽象化のレベルは単純だと思います。コード行がメソッドの単一の責任を直接実装しない場合、それは抽象化のもう1つのレベルです。たとえば、私のメソッド名がSaveChangedCustomers()で、すべての顧客のリストをパラメーターとして取る場合、単一の責任は、リスト内の変更された顧客を保存することです。

foreach(var customer in allCustomers)
{
    if (CustomerIsChanged(customer)
        customer.Save();
}

多くの場合、CustomerIsChanged()メソッドを呼び出す代わりに、顧客が変更したかどうかを判断するロジックがforeachループに埋め込まれています。顧客レコードが変更されたかどうかを判断することは、このメソッドの責任ではありません。それは異なるレベルの抽象化です。しかし、その決定を行うロジックが1行のコードだけである場合はどうでしょうか。それは問題ではありません!!!これは異なるレベルの抽象化であり、このメソッドの外にある必要があります。

1
user7542047