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


35

今日「Clean code」という本を読んでいて、関数ごとの抽象化のレベルについて著者が話している段落に出くわしました。彼はいくつかのコードを低/中/高レベルの抽象化として分類しました。

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

本から段落を引用します。

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


回答:


27

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

[...]プログラムを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
}

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

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


10

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

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

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

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

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

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


2

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

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

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

String pagePathName = PathParser.render(pagePath);文字列を扱います。具体的なもの。部分的には具体的なプログラミング言語機能について。部分的に抽象的な「パス」および「パーサー」の概念を使用します。

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

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

具体的な==言語機能。


1
プログラマが作成するもの、つまりプログラマが生成した抽象化を記述する品質として抽象を定義する場合、私はあなたの単語の定義に同意する傾向があります。しかし、すべてのプログラミング言語は、より具体的なものに対する抽象化です。プログラミング言語の選択により、開始する抽象化のレベルが大幅に決まります。
ロバートハーベイ

1

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.