web-dev-qa-db-ja.com

Clean OOP-Design:単一の責任を実装し、手続き型プログラミングを行わない方法

私は現在、多少手続き的に書かれたC#コードの一部をリファクタリングしようとしています。私は、デザインをクリーンでオブジェクト指向にし、単一の責任を持つクラスを使用したいと考えています。

コードには、Visual Studioソリューションを表し、データのみを含むクラス「Solution」が含まれています。このクラスは、ソリューションのビルド(SolutionBuilderクラス、インターフェイスISolutionBuilder)、ソリューションのNuGetパッケージの更新(クラスNuGetUpdater、インターフェイスINuGetUpdater)、およびSubversionへの変更のコミット(SubversionClientクラス、インターフェイスISubversionClient)を行う他のクラスによって使用されます。 。

現在の実装には次の設計上の欠陥があると思います。

  • クラス「Solution」はデータのみを保持し、動作を伴う実際のOOPクラスではありません
  • 他のクラスもメソッドが1つしかなく、手続き型であるため、実際にはOOPクラスでもありません。
  • コードのにおい:クラスSolutionBuilderとNuGetUpdaterが「er」で終わる

具体的なコード:

public class Solution
{
    public string Name { get; set; }
    public string SolutionFileName { get; set; }
}

public interface ISolutionBuilder
{
    void Build(Solution solution);
}

public interface INuGetUpdater
{
    void Update(Solution solution, string packageSourceUrl, bool safe = true, bool prerelease = false);
}

...

クラスにメソッドが1つしかなくても問題ないことに同意しますが、最近多くのブログを読んで、これは悪いOOPであり、手続き型のコーディングであると述べています。これらのメソッドをソリューションクラスに追加しましたが、これは確かに単一責任の原則に違反し、ソリューションクラスが非常に大きくなると考えました。

私が読んだブログ投稿へのリンク:
あなたのコーディング規約はあなたを傷つけています
'er'で終わるオブジェクトを作成しないでください

これらのブログ投稿の作成者を誤解しましたか?オブジェクト指向の方法でこのコードを構造化する方法を知っている人はいますか?それとも、これが最善の方法なのでしょうか?

6
Ashwani Mehlem

クラスにメソッドが1つしかなくても問題ないことには同意しますが、最近、これが悪いOOPであり、手続き型のコーディングであると指摘する多くのブログを読みました。

クラスにメソッドが1つしかない場合 の場合、通常はExecute()または同等のものです。あなたが自問しなければならない質問は、メソッドが1つしかない場合、クラスを使用して何をカプセル化するかということです。そういうわけでそれらのブログはあなたがそれを再考したいと思うかもしれないと言います。

だから私の最初のアイデアは本当にこれらすべてのメソッドをソリューションクラスに移動することでしたが、これは確かに単一責任の原則に違反し、クラスSolutionをかなり大きくすることになると思いました

「責任」は単に「専門分野」です。 クラスは何かに特化しています。 彼らはその何かで複数のことをするかもしれません。そのバランスがどこにあるかを理解するのはあなた次第です。ボブ・マーティンは、責任は「変更する理由」であり、クラスには変更する理由は1つしかなく、1つの責任を負うべきだと述べています。

これらのブログ投稿の作成者を誤解しましたか?

はい、そうでした。

あなたのコーディング規約はあなたを傷つけています のポイントは、私たちはアーキテクチャの原則に溺れていることであり、それの多くは不要です。

確かに、GOFとSOLIDおよび FactoryFactoryFactory オブジェクトを1日中利用できます。カードの家は美しくなりますが、実際にはどれだけの価値がありますか?

または、コードを書いて何かを行うこともできます。

これを直接目にしたことがあります。最新のアーキテクチャ原則のすべてに従いながら、すべてを正しく実行しているが、まったく柔軟性がなく、推論が困難であり、進化するために一連のコードを必要とするソフトウェアを作成している開発者の軍隊。

一方、小さなアジャイルチームは、 最先端のフレームワーク を使用してシステムの新しいUIを作成していますが、それはおそらくあらゆる種類のアーキテクチャルールに違反し、設計の柔軟性を可能にし、大量の作業を活用します他の人が行うことで、コーディングが大幅に簡素化され、市場投入までの時間が短縮されます。これはプラグマティックであり、ドグマティックではないため、これらのすべてを実行できます。

スティーブ・イェッジは、私が彼よりもよく説明しています "名詞の王国"の記事

For the lack of a nail,
    throw new HorseshoeNailNotFoundException("no nails!");

For the lack of a horseshoe,
    EquestrianDoctor.getLocalInstance().getHorseDispatcher().shoot();

For the lack of a horse,
    RidersGuild.getRiderNotificationSubscriberList().getBroadcaster().run(
      new BroadcastMessage(StableFactory.getNullHorseInstance()));

For the lack of a rider,
    MessageDeliverySubsystem.getLogger().logDeliveryFailure(
      MessageFactory.getAbstractMessageInstance(
        new MessageMedium(MessageType.VERBAL),
        new MessageTransport(MessageTransportType.MOUNTED_RIDER),
        new MessageSessionDestination(BattleManager.getRoutingInfo(
                                        BattleLocation.NEAREST))),
      MessageFailureReasonCode.UNKNOWN_RIDER_FAILURE);

For the lack of a message,
    ((BattleNotificationSender)
      BattleResourceMediator.getMediatorInstance().getResource(
        BattleParticipant.PROXY_PARTICIPANT,
        BattleResource.BATTLE_NOTIFICATION_SENDER)).sendNotification(
          ((BattleNotificationBuilder)
            (BattleResourceMediator.getMediatorInstance().getResource(
            BattleOrganizer.getBattleParticipant(Battle.Participant.GOOD_GUYS),
            BattleResource.BATTLE_NOTIFICATION_BUILDER))).buildNotification(
              BattleOrganizer.getBattleState(BattleResult.BATTLE_LOST),
              BattleManager.getChainOfCommand().getCommandChainNotifier()));

For the lack of a battle,
    try {
        synchronized(BattleInformationRouterLock.getLockInstance()) {
          BattleInformationRouterLock.getLockInstance().wait();
        }
    } catch (InterruptedException ix) {
      if (BattleSessionManager.getBattleStatus(
           BattleResource.getLocalizedBattleResource(Locale.getDefault()),
           BattleContext.createContext(
             Kingdom.getMasterBattleCoordinatorInstance(
               new TweedleBeetlePuddlePaddleBattle()).populate(
                 RegionManager.getArmpitProvince(Armpit.LEFTMOST)))) ==
          BattleStatus.LOST) {
        if (LOGGER.isLoggable(Level.TOTALLY_SCREWED)) {
          LOGGER.logScrewage(BattleLogger.createBattleLogMessage(
            BattleStatusFormatter.format(BattleStatus.LOST_WAR,
                                         Locale.getDefault())));
        }
      }
    }

For the lack of a war,
    new ServiceExecutionJoinPoint(
      DistributedQueryAnalyzer.forwardQueryResult(
        NotificationSchemaManager.getAbstractSchemaMapper(
          new PublishSubscribeNotificationSchema()).getSchemaProxy().
            executePublishSubscribeQueryPlan(
              NotificationSchema.ALERT,
              new NotificationSchemaPriority(SchemaPriority.MAX_PRIORITY),
              new PublisherMessage(MessageFactory.getAbstractMessage(
                MessageType.WRITTEN,
                new MessageTransport(MessageTransportType.WOUNDED_SURVIVOR),
                new MessageSessionDestination(
                  DestinationManager.getNullDestinationForQueryPlan()))),
              DistributedWarMachine.getPartyRoleManager().getRegisteredParties(
                PartyRoleManager.PARTY_KING ||
                PartyRoleManager.PARTY_GENERAL ||
                PartyRoleManager.PARTY_AMBASSADOR)).getQueryResult(),
        PriorityMessageDispatcher.getPriorityDispatchInstance())).
      waitForService();

All for the lack of a horseshoe nail.

あなたの頭がそれを見て痛いのなら、まあ...それも私のものを痛めます。

'er'で終わるオブジェクトを作成しないでください のポイントは、クラスがerで終わる場合、その機能を実際のオブジェクトに入れた方がよい場合があるということですあなたのヘルパークラスが助けています。それで全部です。

最後の注意:正しい方法も間違った方法もありません。ソフトウェア要件とビジネスニーズに最適な方法しかありません。Javaショップなので、すべてのセレモニーが機能する場合、レガシーコードはすべての名詞の王国と誰もがそれを理解し、それから必ず、その道を進みます。

ただし、これが唯一の方法ではありません。

7
Robert Harvey