論理的に手続き型のソフトウェアをオブジェクト指向言語で記述する最もクリーンな方法


12

私は電気技師で、一体何をしているのかわかりません。私のコードの将来のメンテナーを保存してください。

最近、機能が論理的に「手続き型」である(C#の)いくつかの小さなプログラムに取り組んでいます。たとえば、その1つは、さまざまなデータベースから情報を収集し、その情報を使用して一種の要約ページを生成し、印刷してから終了するプログラムです。

これらすべてに必要なロジックは約2000行です。以前の開発者が行っていたように、私はそれらすべてを1つにmain()詰め込んで#regionsで「クリーンアップ」したくありません(シャダー)。

ここに私がすでに満足していないいくつかの試みがあります:

DatabaseInfoGetter、SummaryPageGenerator、PrintUtilityなど、機能の粗いビットごとに静的ユーティリティを作成します。メイン関数を次のようにします。

int main()
{
    var thisThing = DatabaseInfoGetter.GetThis();
    var thatThing = DatabaseInfoGetter.GetThat();
    var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

    PrintUtility.Print(summary);
}

1つのプログラムでは、インターフェイスも使用しました

int main()
{
    /* pardon the psuedocode */

    List<Function> toDoList = new List<Function>();
    toDoList.Add(new DatabaseInfoGetter(serverUrl));
    toDoList.Add(new SummaryPageGenerator());
    toDoList.Add(new PrintUtility());

    foreach (Function f in toDoList)
        f.Do();
}

これはどれも正しくないと感じています。コードが長くなるにつれて、これらのアプローチはどちらも醜くなり始めます。

このようなものを構造化する良い方法は何ですか?



1
おそらく興味があるかもしれません
Dan Pichelman、2015年

@Snowman、より大きなコードを書くのが初めての人として、この種の問題に遭遇したのは私だけではないことを知って安心します。その質問に対するあなたの答えが好きだった。助けになる。
ロンバード

@Lombardは、複雑さを扱いやすいレベルまで下げるというアイデアは、開発するのに本当に良いスキルです。誰も大きなコードベースではなく、さらにスーパーマン(多分バットマンけれども)を理解することはできません。アイデアを簡単に表現し、コードを自己文書化する方法を見つけることが重要です。

「最近、機能が論理的に「手続き型」であるいくつかの小さなプログラム(C#で)に取り組んでいます。たとえば、その1つは、さまざまなデータベースから情報を収集し、その情報を使用して一種の要約を生成するプログラムです。ページ、それを印刷してから終了します。 ":「手続き型」と「命令型」を混同していないのですか?手続き型とオブジェクト指向の両方が必須です(つまり、必須)。つまり、シーケンスで実行される操作を表現できます。
ジョルジオ

回答:


18

あなたはプロのプログラマーではないので、シンプルさを守ることをお勧めします。プログラマは、モジュール化された手続き型コードを取得して後でオブジェクト指向にするのが、不適切に記述されたオブジェクト指向プログラムを修正するよりも、はるかに簡単になります。経験の浅い場合は、OOプログラムを作成して、あなたやあなたの後に来る人を助けることのできない不気味な混乱に変えることができます。

私はあなたの最初の本能、最初の例の「これ-そのこと」のコードが正しい道だと思います。あなたが何をしたいかは明確で明白です。コードの効率についてあまり気にしないでください。わかりやすさがはるかに重要です。

コードセグメントが長すぎる場合は、それぞれに独自の機能を持つ一口サイズのチャンクに分割します。短すぎる場合は、使用するモジュールを減らし、より多くのモジュールを配置することを検討してください。

----追記:OOデザイントラップ

OOプログラミングをうまく使用することは難しい場合があります。モデル全体に​​欠陥があると考える人もます。OOプログラミングを初めて学ぶときに使用した、Javaで考えるという本(現在は第4版)があります。同じ著者がC ++の対応する本を持っています。オブジェクト指向プログラミングの一般的な落とし穴を扱うプログラマーには、実際には別の質問があります

いくつかの落とし穴は洗練されていますが、非常に基本的な方法で問題を作成する方法はたくさんあります。例えば、数年前、私は、継承されたいくつかのソフトウェアの最初のバージョンを書いて、彼はすべてそのためのインタフェースを作った私の会社でのインターンがあったかもしれないがいつかは複数の実装があります。もちろん、98%のケースでは実装が1つしかなかったため、未使用のインターフェースを使用してコードをロードしたため、インターフェースの呼び出しをステップバックできないため、デバッグが非常に煩わしくなり、結果的に実装のテキスト検索(現在はIntelliJを使用していましたが、[すべての実装を表示]機能がありましたが、当時はありませんでした)。ここでのルールは、手続き型プログラミングの場合と同じです。常に1つをハードコードします。2つ以上のものがある場合にのみ、抽象化を作成します。

同様の種類の設計の失敗は、Java Swing APIで見つけることができます。Swingメニューシステムのパブリッシュサブスクライブモデルを使用します。これにより、Swingでのメニューの作成とデバッグは完全な悪夢になります。皮肉なことに、それは完全に無意味です。複数の機能がメニュークリックに「サブスクライブ」する必要がある状況は、ほとんどありません。また、通常は常にメニューシステムが使用されているため、パブリッシュサブスクライブは完全に失敗しました。これは、関数がランダムにサブスクライブしてからサブスクライブ解除するようなものではありません。Sunの「プロの」開発者がこのような失敗をしたという事実は、プロがオブジェクト指向の設計で巨大なねじ込みを作るのがいかに簡単かを示しています。

私はオブジェクト指向プログラミングで数十年の経験を持つ非常に専門的な開発者ですが、知らないトンがあることを最初に認めたとしても、今でも多くのオブジェクト指向の使用には非常に慎重です。私は、OOデザインの熱心な同僚から、特定のデザインを行う方法について長い講義を聞いていました。彼は自分が何をしているかを本当に知っていましたが、正直なところ、そのような洗練されたデザインモデルがあったため、私は彼のプログラムを理解するのに苦労しました。


1
「明快さは効率よりもはるかに重要」の+1
Stephen

「不適切に記述されたOOプログラム」を構成するものを説明するリンクを教えてもらえますか、それについて編集にいくつかの情報を追加してください。
ロンバード

@ロンバード私はそれをやった。
タイラーダーデン2015年

1

最初のアプローチは問題ありません。Cのような手続き型言語では、標準的なアプローチは機能を名前空間に分割することです-のような静的クラスを使用するDatabaseInfoGetterことは基本的に同じことです。明らかに、このアプローチは、すべてがメソッドに分解されたとしても、すべてを1つのクラスに入れるよりも、より単純で、よりモジュール化され、より読みやすく、保守可能なコードになります。

そういえば、メソッドをできるだけ少ないアクションに限定するようにしてください。一部のプログラマーは少し細かさを優先しますが、巨大なメソッドは常に有害と見なされます。

それでも複雑さに苦労している場合は、おそらくプログラムをさらに分解する必要があります。階層内により多くのレベルを作成します-おそらく何かDatabaseInfoGetter他のクラスを参照する必要がありますProductTableEntry。あなたは手続き型コードを書いていますが、C#を使用していて、OOPは複雑さを軽減するための一連のツールを提供します。

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

また、静的クラスにも注意してください。データベースクラスは良い候補です。通常、データベースが1つある限り、アイデアがわかります。

最終的に数学関数を考え、C#が自分のスタイルに合わない場合は、ScalaやHaskellなどを試してみてください。


0

私は4年遅れで応答していますが、OO言語で手続き型のことをしようとしているときは、アンチパターンに取り組んでいます。それはあなたがすべきことではありません!このアンチパターンに対処し、その解決策を示すブログを見つけましたが、多くのリファクタリングと考え方の変更が必要です。詳細については、オブジェクト指向コードの手続き型コードを参照してください。
"this"と "that"について述べたように、基本的には2つの異なるクラスがあることを示唆しています。A Thisクラス(悪い名前!)とThatクラス。そしてあなたは例えばこれを翻訳することができます:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

これに:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

ここで、2,000行以上の手続き型コードを見て、さまざまな部分を別々のクラスにグループ化し、さまざまな手続きをそれらのクラスに移動する方法を決定するのは興味深いことです。
各クラスはシンプルで、1つのことだけに集中する必要があります。そして私には、コードの一部がすでにスーパークラスを処理しているように見えますが、それらは実際には大きすぎて便利ではありません。たとえば、DatabaseInfoGetterは混乱を招くように聞こえます。とにかくそれは何を取得していますか?汎用的すぎるようです。しかし、SummaryPageGeneratorは非常に具体的です!そして、PrintUtilityが再び一般的になりました。
したがって、開始するには、クラスの総称名を避け、代わりに、より具体的な名前を使用する必要があります。たとえば、PrintUtilityクラスは、Headerクラス、Footerクラス、TextBlockクラス、Imageクラスを備えたPrintクラスであり、おそらく印刷自体の中でどのようにフローするかを示す何らかの方法です。これはすべて、ただし、PrintUtility 名前空間
データベースについても同様です。Entity Frameworkをデータベースアクセスに使用する場合でも、使用するものに制限し、論理グループに分割する必要があります。
しかし、4年経ってもこの問題に対処しているのでしょうか。もしそうなら、それは「手続き型概念」から「オブジェクト指向概念」に変更する必要がある考え方です。あなたが経験を積んでいない場合、これは挑戦的です...


-3

私は私の意見です。コードを変更して、シンプルで一貫性があり、読みやすいものにする必要があります。

私はこのトピックについていくつかのブログ投稿を書いており、それらが理解するのは簡単であると信じています: 良いコードとは

シンプル:回路基板にさまざまな部品が含まれているように、それぞれに責任があります。コードは、より小さく単純な部分に分割する必要があります。

一貫性:要素やものに名前を付けるための標準であるパターンを使用します。いつも同じように機能するツールが好きで、どこにそれらがあるかを知りたいと思っています。コードも同じです。

読みやすい:ソフトウェアが対象とするドメイン(mattnz)内で広く使用され、知られている場合を除き、頭字語は使用しないでください。明確で理解しやすい発音の良い名前を使用してください。


1
頭字語は、ソフトウェアが対象としているドメイン内で広く使用され、知られている場合は許容されます。を使用せずに航空管制ソフトウェアを書いてみてください-私たちは頭字語から作られた頭字語を持っています-あなたが話していることを誰もあなたが完全な名前を使っていたのかわからないでしょう。ネットワーキングのHTTP、自動車のABSなどは完全に許容されます。
mattnz、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.