apeescape2.com
  • メイン
  • 財務プロセス
  • デザイナーライフ
  • プロセスとツール
  • ブランドデザイン
バックエンド

単一責任の原則:優れたコードのレシピ

優れたコードと見なされるものに関係なく、常に1つの単純な品質が必要です。コードは保守可能でなければなりません。適切なインデント、きちんとした変数名、100%のテストカバレッジなどは、これまでのところしか理解できません。保守が不可能で、要件の変化に比較的簡単に適応できないコードは、廃止されるのを待っているコードです。プロトタイプ、概念実証、または最小限の実行可能な製品を構築しようとするときに、優れたコードを作成する必要はないかもしれませんが、それ以外の場合は常に、保守可能なコードを作成する必要があります。これは、ソフトウェアエンジニアリングと設計の基本的な品質と見なされるべきものです。

単一責任の原則:優れたコードのレシピ

この記事では、単一責任の原則とそれを中心に展開するいくつかの手法によって、コードにこれほどの品質を与える方法について説明します。優れたコードを書くことは芸術ですが、いくつかの原則は、堅牢で保守可能なソフトウェアを作成するために必要な方向性を開発作業に与えるのに常に役立ちます。



モデルがすべてです

新しいMVC(MVP、MVVM、またはその他のM **)フレームワークに関するほとんどすべての本には、不正なコードの例が散らばっています。これらの例は、フレームワークが提供するものを示しています。しかし、彼らはまた、初心者に悪いアドバイスを提供することになります。 「モデルにこのORMXを使用し、ビューにエンジンYをテンプレート化し、すべてを管理するコントローラーを用意するとします」などの例では、巨大なコントローラー以外の何物でもありません。

メモリリークがあるかどうかを見分ける方法

これらの本を擁護していますが、これらの例は、フレームワークを簡単に使い始めることができることを示すことを目的としています。それらはソフトウェア設計を教えることを意図していません。しかし、これらの例に従う読者は、数年後になって初めて、プロジェクトにモノリシックなコードのチャンクを含めることがいかに逆効果であるかを理解しています。

モデルはアプリの心臓部です。

モデルはアプリの心臓部です。モデルを他のアプリケーションロジックから分離している場合は、アプリケーションがどれほど複雑になっても、メンテナンスがはるかに簡単になります。複雑なアプリケーションの場合でも、優れたモデル実装により、非常に表現力豊かなコードが得られます。それを実現するには、モデルが意図したとおりに動作することを確認することから始めます。モデルを中心に構築されたアプリの動作には関心がありません。さらに、基盤となるデータストレージレイヤーが何であるかには関係ありません。アプリはSQLデータベースに依存していますか、それともすべてをテキストファイルに保存していますか?

この記事を続けると、関心の分離に関してコードがいかに優れているかがわかります。

単一責任の原則

SOLIDの原則について聞いたことがあると思います。単一責任、オープンクローズ、リスコフの置換、インターフェイスの分離、依存関係の逆転です。最初の文字Sは、単一責任原則(SRP)を表しており、その重要性を誇張することはできません。私はそれが良いコードのための必要十分条件であるとさえ主張するでしょう。実際、ひどく書かれたコードでは、常に複数の責任を持つクラスを見つけることができます-数千行のコードを含むform1.csまたはindex.phpは、私たち全員にとって珍しいことではありませんおそらくそれを見たか、やったことがあるでしょう。

C#(ASP.NET MVCおよびEntity Framework)の例を見てみましょう。あなたがいない場合でも C#開発者 、OOPの経験があれば、簡単にフォローできるようになります。

public class OrderController { ... public ActionResult CreateForm() { /* * View data preparations */ return View(); } [HttpPost] public ActionResult Create(OrderCreateRequest request) { if (!ModelState.IsValid) { /* * View data preparations */ return View(); } using (var context = new DataContext()) { var order = new Order(); // Create order from request context.Orders.Add(order); // Reserve ordered goods …(Huge logic here)... context.SaveChanges(); //Send email with order details for customer } return RedirectToAction('Index'); } ... (many more methods like Create here) }

これは通常のOrderControllerクラスであり、そのCreateメソッドが示されています。このようなコントローラーでは、Orderクラス自体が要求パラメーターとして使用される場合がよくあります。しかし、私は特別なリクエストクラスを使用することを好みます。繰り返しますが、SRP!

1つのコントローラーに対してジョブが多すぎます

上記のコードスニペットで、コントローラーが「注文」についてあまりにも多くのことを知っていることに注意してください。これには、Orderオブジェクトの保存、電子メールの送信などが含まれますが、これらに限定されません。これは、単一のクラスには単純に多すぎるジョブです。開発者は、小さな変更を加えるたびに、コントローラーのコード全体を変更する必要があります。また、別のコントローラーも注文を作成する必要がある場合に備えて、開発者はコードをコピーして貼り付けることになります。コントローラはプロセス全体を制御するだけで、実際にはプロセスのロジックのすべてのビットを格納する必要はありません。

しかし、今日はこれらの巨大なコントローラーの作成をやめる日です!

誰かのクレジットカード情報をハッキングする方法

まず、コントローラーからすべてのビジネスロジックを抽出し、それをOrderServiceクラスに移動しましょう。

public class OrderService { public void Create(OrderCreateRequest request) { // all actions for order creating here } } public class OrderController { public OrderController() { this.service = new OrderService(); } [HttpPost] public ActionResult Create(OrderCreateRequest request) { if (!ModelState.IsValid) { /* * View data preparations */ return View(); } this.service.Create(request); return RedirectToAction('Index'); }

これが完了すると、コントローラーは意図したこと、つまりプロセスの制御のみを実行するようになります。ビュー、OrderServiceクラス、およびOrde​​rRequestクラスについてのみ認識します。これは、要求の管理と応答の送信というジョブを実行するために必要な最小限の情報セットです。

この方法では、コントローラーコードを変更することはめったにありません。ビュー、リクエストオブジェクト、サービスなどの他のコンポーネントは、ビジネス要件にリンクされているため変更できますが、コントローラーにはリンクされていません。

これがSRPの目的であり、この原則を満たすコードを作成するための多くの手法があります。この一例は、依存性注入です(これは、 テスト可能なコードを書く )。

依存性注入

依存性注入のない単一責任原則に基づく大規模なプロジェクトを想像するのは難しいです。 OrderServiceクラスをもう一度見てみましょう。

public class OrderService { public void Create(...) { // Creating the order(and let’s forget about reserving here, it’s not important for following examples) // Sending an email to client with order details var smtp = new SMTP(); // Setting smtp.Host, UserName, Password and other parameters smtp.Send(); } }

このコードは機能しますが、理想的ではありません。 createメソッドOrderServiceクラスがどのように機能するかを理解するために、SMTPの複雑さを理解する必要があります。また、このSMTPの使用を必要な場所に複製するには、コピーアンドペーストが唯一の方法です。しかし、少しリファクタリングすることで、それは変わる可能性があります。

javascripttypeerrorは関数ではありません
public class OrderService { private SmtpMailer mailer; public OrderService() { this.mailer = new SmtpMailer(); } public void Create(...) { // Creating the order // Sending an email to client with order details this.mailer.Send(...); } } public class SmtpMailer { public void Send(string to, string subject, string body) { // SMTP stuff will be only here } }

すでにはるかに良いです!しかし、OrderServiceクラスはまだ電子メールの送信について多くのことを知っています。電子メールを送信するには、正確にSmtpMailerクラスが必要です。将来変更したい場合はどうなりますか?開発環境で実際に送信するのではなく、送信された電子メールの内容を特別なログファイルに印刷したい場合はどうなりますか? OrderServiceクラスをユニットテストしたい場合はどうなりますか?インターフェイスIMailerを作成して、リファクタリングを続けましょう。

public interface IMailer { void Send(string to, string subject, string body); }

SmtpMailerはこのインターフェースを実装します。また、アプリケーションはIoCコンテナーを使用し、IMailerがSmtpMailerクラスによって実装されるように構成できます。 OrderServiceは、次のように変更できます。

public sealed class OrderService: IOrderService { private IOrderRepository repository; private IMailer mailer; public OrderService(IOrderRepository repository, IMailer mailer) { this.repository = repository; this.mailer = mailer; } public void Create(...) { var order = new Order(); // fill the Order entity using the full power of our Business Logic(discounts, promotions, etc.) this.repository.Save(order); this.mailer.Send(, , ); } }

今、私たちはどこかに到達しています!私はこの機会にまた別の変更を加えました。 OrderServiceは、すべての注文を格納するコンポーネントと対話するためにIOrderRepositoryインターフェースに依存するようになりました。そのインターフェースがどのように実装され、どのストレージテクノロジーがそれを支えているかについてはもはや気にしません。現在、OrderServiceクラスには、注文ビジネスロジックを処理するコードのみが含まれています。

このようにして、テスターが電子メールの送信で正しく動作しないものを見つけた場合、開発者はどこを見ればよいかを正確に知ることができます:SmtpMailerクラス。割引に問題があった場合、開発者はここでも、OrderService(または、SRPを心から採用している場合は、DiscountServiceの可能性があります)クラスコードを確認します。

イベント駆動型アーキテクチャ

ただし、OrderService.Createメソッドはまだ好きではありません。

public void Create(...) { var order = new Order(); ... this.repository.Save(order); this.mailer.Send(, , ); }

メールの送信は、メインの注文作成フローの一部ではありません。アプリがメールの送信に失敗した場合でも、注文は正しく作成されます。また、注文が正常に行われた後、ユーザーが電子メールの受信をオプトアウトできるようにする新しいオプションをユーザー設定領域に追加する必要がある状況を想像してみてください。これをOrderServiceクラスに組み込むには、依存関係IUserParametersServiceを導入する必要があります。ローカリゼーションをミックスに追加すると、さらに別の依存関係であるITranslatorがあります(ユーザーが選択した言語で正しい電子メールメッセージを生成するため)。これらのアクションのいくつかは不要です。特に、これらの多くの依存関係を追加して、画面に収まらないコンストラクターで終わるという考えは不要です。私は見つけました 素晴らしい例 これは、32の依存関係を持つクラスのMagentoのコードベース(PHPで記述された人気のあるeコマースCMS)にあります。

画面に収まらないコンストラクター

このロジックを分離する方法を理解するのが難しい場合があり、Magentoのクラスはおそらくそれらのケースの1つの犠牲者です。それが私がイベント駆動型の方法が好きな理由です:

不和のためのボットの作り方
namespace .Events { [Serializable] public class OrderCreated { private readonly Order order; public OrderCreated(Order order) { this.order = order; } public Order GetOrder() { return this.order; } } }

注文が作成されるたびに、OrderServiceクラスから直接電子メールを送信する代わりに、特別なイベントクラスOrderCreatedが作成され、イベントが生成されます。アプリケーションのどこかにイベントハンドラーが構成されます。そのうちの1つがクライアントにメールを送信します。

namespace .EventHandlers { public class OrderCreatedEmailSender : IEventHandler { public OrderCreatedEmailSender(IMailer, IUserParametersService, ITranslator) { // this class depend on all stuff which it need to send an email. } public void Handle(OrderCreated event) { this.mailer.Send(...); } } }

クラスOrderCreatedは、意図的にSerializableとしてマークされています。このイベントをすぐに処理することも、シリアル化されたキュー(Redis、ActiveMQなど)に保存して、Webリクエストを処理するプロセス/スレッドとは別のプロセス/スレッドで処理することもできます。に この記事 著者は、イベント駆動型アーキテクチャとは何かを詳細に説明しています(OrderController内のビジネスロジックには注意を払わないでください)。

注文を作成するときに何が起こっているのかを理解するのが今では難しいと主張する人もいるかもしれません。しかし、それは真実からこれ以上離れることはできません。そのように感じた場合は、IDEの機能を利用してください。 IDEでOrderCreatedクラスのすべての使用法を見つけることにより、イベントに関連するすべてのアクションを確認できます。

C ++の学習を開始する方法

しかし、いつ依存性注入を使用する必要があり、いつイベント駆動型アプローチを使用する必要がありますか?この質問に答えるのは必ずしも簡単ではありませんが、アプリケーション内のすべての主要なアクティビティに依存性注入を使用し、すべての二次アクションにイベント駆動型アプローチを使用するのに役立つ可能性のある簡単なルールが1つあります。たとえば、IOrderRepositoryを使用してOrderServiceクラス内で注文を作成したり、メインの注文作成フローの重要な部分ではない電子メールの送信をイベントハンドラーに委任したりするなど、依存性注入を使用します。

結論

最初は非常に重いコントローラー、たった1つのクラスから始めて、最終的には手の込んだクラスのコレクションになりました。これらの変更の利点は、例から非常に明白です。ただし、これらの例を改善する方法はまだたくさんあります。たとえば、OrderService.Createメソッドを独自のクラスOrderCreatorに移動できます。注文の作成は、単一責任の原則に従った独立したビジネスロジックの単位であるため、独自の依存関係を持つ独自のクラスを持つのは当然のことです。同様に、注文の削除とキャンセルは、それぞれ独自のクラスで実装できます。

この記事の最初の例に似た、高度に結合されたコードを書いたとき、要件を少し変更すると、コードの他の部分に多くの変更が加えられる可能性があります。 SRPは、開発者が分離されたコードを作成するのに役立ちます。各クラスには独自の仕事があります。このジョブの仕様が変更された場合、開発者はその特定のクラスにのみ変更を加えます。もちろん、そもそも壊れていない限り、他のクラスは以前と同じように仕事をしているはずなので、この変更によってアプリケーション全体が壊れる可能性は低くなります。

これらの手法を使用してコードを事前に開発し、単一責任の原則に従うことは困難な作業のように思えるかもしれませんが、プロジェクトが成長し、開発が継続するにつれて、努力は確実に報われるでしょう。

MetaDapper:適切なツールでデータのマッピングと変換が簡単に

データサイエンスとデータベース

MetaDapper:適切なツールでデータのマッピングと変換が簡単に
ヘッジファンドのディープラーニングトレーディング入門

ヘッジファンドのディープラーニングトレーディング入門

バックエンド

人気の投稿
シニアコーポレートカウンセル
シニアコーポレートカウンセル
エンタープライズクライアントサービスディレクター、産業用製品およびサービス
エンタープライズクライアントサービスディレクター、産業用製品およびサービス
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
フリーランスのファイナンスコンサルタントが大企業をどのように打ち負かしているか
あなたのスキルを披露する–ポートフォリオを作成する方法
あなたのスキルを披露する–ポートフォリオを作成する方法
Reactフックをテストするための完全ガイド
Reactフックをテストするための完全ガイド
 
かんばんとTrelloを使用してソフトウェア開発を管理するための初心者向けガイド
かんばんとTrelloを使用してソフトウェア開発を管理するための初心者向けガイド
生存のための再編成:シナリオの構築
生存のための再編成:シナリオの構築
Android開発のためのReactNativeに飛び込む
Android開発のためのReactNativeに飛び込む
安全で健全–パスワードUXへのアプローチ方法
安全で健全–パスワードUXへのアプローチ方法
.NETプロジェクトをブートストラップして作成する方法
.NETプロジェクトをブートストラップして作成する方法
人気の投稿
  • いつCFOを雇うか
  • グーグルクラウド自然言語API
  • cとs法人の違い
  • IoTとスマートホームテクノロジー
  • 各ユニットは、のフォーマットを作成します
  • デザインの原則のパターン
カテゴリー
製品の担当者とチーム 技術 データサイエンスとデータベース ブランドデザイン モバイル その他 アジャイルタレント 計画と予測 プロセスとツール アジャイル

© 2021 | 全著作権所有

apeescape2.com