JavaScriptにおける立体設計原理(第3部)—界面偏析原理と依存性反転


良いデザインは明白です.大きなデザインは透明です.
歓迎の人々は、あなたが今までこのシリーズを楽しんでいた願っています.これはシリーズの最終的な部分です.前の記事では、2番目と3番目の立体設計原理(オープンクローズ原理とliskov置換原理)を見ました.界面偏析原理と依存性反転である最後の2つの原理に焦点を当てた.
この記事のパート1またはパート2を読んでいない場合は、ここにアクセスできます.
  • 第1部
  • 第2部
  • また、私のgithubの上の完全なコード例にアクセスすることもできます
    https://github.com/Caleb-Mantey/solid-design-principles-in-js
    今すぐ私たちのコードがどのように見えるかを要約することができます

    メーラー


    class Mailer{
            constructor(mail, mailerFormats){
                this.mail = mail
                this.mailerFormats = mailerFormats
                this.smtpService = new PostMarkSmtpService()
                // OR this.smtpService = new SendGridSmtpService()
            }    
    
            send(){
                // Loops through mail formats and calls the send method
                this.mailerFormats.forEach((formatter) => 
                this.smtpService.send(formatter.format(this.mail)))
            }
    }
    

    メールサービス


    class MailerSmtpService{
            constructor(smtp_connection = () => {
                //connects to default smtp service
            }){
               this.smtp_con = smtp_connection()
            }   
    
            send (mail){
                this.smtp_con.send(mail)
            }
    }
    

    PostMarksmtPService


    class PostMarkSmtpService extends MailerSmtpService {
            constructor(){
               super(() => {
                    // Connects to postmark smtp service
                })
            }    
    
            send (mail){
                this.smtp_con.send(mail)
            }
    }
    

    SendGridDSMTPサービス


    class SendGridSmtpService extends MailerSmtpService {
            constructor(){
                super(() => {
                    // Connects to sendgrid smtp service
                })
            }      
    
            send (mail){
                this.smtp_con.deliver(mail)
            }
    }
    

    HTMFormatter


    class HtmlFormatter{
            constructor(){
            }        
    
            format(mail){
                 // formats to html version of mail
                 mail = `<html>
                <head><title>Email For You</title></head>
                <body>${mail}</body>
                </html>`;            
                return mail;
            }
    }
    

    テキストフォーマッタ


    class TextFormatter{
            constructor(){
            }   
    
            format(mail){
                 // formats to text version of mail
                 mail = "Email For You \n" + mail;          
                 return mail;
            }
    }
    
    上記のコードは次のようにします.
  • SMTPサービスの動作を設定する基本クラス( MailErsmtService )
  • MailErsmtPServiceクラスから継承し、SMTPサービスに接続する子クラス( postMarksmtService )
  • mailerSMTPサービスクラスから継承し、SMTPサービス( SendDridDSMTPサービス)に接続する子クラス
  • テキストをテキスト形式でフォーマットするクラス( TextFormatter )
  • HTMLでメールをフォーマットするクラス
  • メールを送るクラス
  • このコードは、それがインスタンス化され、このようなメールを送信するために使用することができます私たちのメーラークラスで一緒に結ばれます.
    const mailer = new Mailer(hello kwame, [new HtmlFormatter(), new TextFormatter()])
    mailer.send();
    
    これは、我々がこの記事のパート1と2から持っていた実装です.あなたがそれを読んでいないならば、私はパート1と2を読むことを勧めます.あなたは、このテキストの冒頭に提供されるリンクを訪問することによってそうすることができます.

    界面分離原理


    この原理は

    Do not force any client to implement an interface which is irrelevant to them.


    この原理はsingle responsibility principle がインターフェイスにも適用される.通常、インタフェースの第1の原理と呼ばれる.JavaScriptはインターフェイスをサポートしていないので、より良い理解を得るためにTypesScriptで実装します.最初の例を見ましょうHtmlFormatter and TextFormatter 我々の電子メールをフォーマットして、若干の少しの変更をするクラス.

    フォーマッタ


    export interface IFormatter {
            format(mail: string): string
            custom_styles(): string
    }
    

    HTMFormatter


    class HtmlFormatter implements IFormatter {
    
           format(mail: string) {
                // sends html version of mail
                mail = `<html>
                <head>
                <title>Email For You</title>
                ${this.custom_styles()}
                </head>
                <body>${mail}</body>
                </html>`;
                return mail;
            }
    
            custom_styles(): string {
                return "<style>body{background-color: blue}</style>"
            }
    }
    

    テキストフォーマッタ


    class TextFormatter implements IFormatter {
    
            format(mail: string) {
                // sends text version of mail
                mail = "Text Version \n" + mail;
                return mail;
            }
    
            custom_styles(): string {
                return ""
            }
    }
    
    TypesScriptを含めて、関数の戻り値の型と変数のデータ型を宣言する必要があります.我々はまた、Cのような言語でのようなインターフェイスを作成する能力があります.
    この機能を使用すると、我々はインターフェイスを追加しましたIFormatter ) それは2つの機能を公開するformat and custom_styles ). 我々TextFormatter and HtmlFormatter クラスもこのインターフェースの実装ですIFormatter ) メソッド.これは確かに我々はformat and custom_styles メソッドは両方で実装されてTextFormatter and HtmlFormatter クラス.If the format and custom_styles メソッドは、IFormatter インターフェイス、我々のアプリケーションのエラーをスローします.しかし、ここに問題がありますcustom_styles メソッドは、HtmlFormatter HTMLドキュメントのスタイルを支援するクラスです.しかし両方ともTextFormatter and HtmlFormatter クラスは同じインターフェイスを使用しますIFormatter ) 両方とも同じメソッドを実装しなければなりません(custom_styles and format ) 強制的に空を書くcustom_styles メソッドTextFormatter クラス.
    では、より良いアプローチを見てみましょう.

    スタイル


    export interface IStyles {
            custom_styles(): string
    }
    

    フォーマッタ


    export interface IFormatter {
            format(mail: string): string
    }
    

    HTMFormatter


    class HtmlFormatter implements IFormatter, IStyles {
           format(mail: string) {
                // sends html version of mail
                mail = `<html>
                <head>
                <title>Email For You</title>
                ${this.custom_styles()}
                </head>
                <body>${mail}</body>
                </html>`;
                return mail;
            }
    
            custom_styles(): string {
                return "<style>body{background-color: blue}</style>"
            }
    }
    

    テキストフォーマッタ


    class TextFormatter implements IFormatter {
            format(mail: string) {
                // sends text version of mail
                mail = "Text Version \n" + mail;
                return mail;
            }
    }
    
    今、あなたは我々が新しいインターフェースを持っているコードrefactorから見ることができますIStyles だけでなく、我々の前のインターフェイスIFormatter . またHtmlFormatter クラスはIStyles and IFormatter インターフェースTextFormatter クラスはIFormatter インターフェイス.これで、コードをクリーンにし、適切なメソッドをそれらを必要とするクラスで実装することができます.現在我々TextFormatter クラスを実装する必要はありませんcustom_styles メソッドを削除しましたcustom_styles からのメソッドIFormatter 新しいインターフェイスへのインタフェースIStyles ). これにより、コードはより保守的でスケーラブルになります.これは仕事中の界面分離原理です.

    依存性反転原理


    この原理は2つの部分に分けられ

    High-level modules/classes should not depend on low-level modules/classes. Both should depend on abstractions.
    Abstractions should not depend on details. Details should depend on abstractions.


    上記の行は単にハイレベルのモジュールやクラスが低レベルのモジュールやクラスに依存している場合、コードがタイトな結合を持っていて、1つのクラスを変更しようとすると別のクラスを壊すことができます.それは常に抽象的にクラスを緩く結合できるようにコードを抽象化することができます.これは、コードを簡単に維持することができます.
    依存関係の反転が単に依存性注入を言う別の方法であるという一般的な誤解があります.しかし、2つは同じではありません.
    前の例では、2つの新しいインターフェースを作成しましたIStyles and IFormatter この関数はTextFormatter and HtmlFormatter クラス.
    以下の例で、これらのクラスを抽象化でどのように使用するかを確認できます.

    メーラー


    class Mailer {
        mail: string;
        mailerFormats: Array<IFormatter>; // abstraction
        smtpService: MailerSmtpService;
    
        constructor(mail: string, mailerFormats: Array<IFormatter>/*abstraction*/) {
            this.mail = mail;
            this.mailerFormats = mailerFormats;
            this.smtpService = new SendGridSmtpService();
        }
    
        send() {
            // Loops through mail formats and calls the send method
            this.mailerFormats.forEach((formatter) =>
            this.smtpService.send(formatter.format(this.mail))
            );
        }
    }
    
    では、次のリファクタを見てみましょうMailer 私たちの最初の例(第一原理-単一の責任原則)からのクラス.あなたは、我々が現在持っているのを見ることができますmailerFormats 配列を取得するプロパティIFormatter オブジェクトmailerFormats: Array<IFormatter>; ). これは、IFormatter インターフェイスをこの配列に格納できます.その他Mailer クラスは、どのフォーマッタを使用するかを知る必要はありません.すべてが気にかけるのはフォーマッタがIFormatter インターフェイスとformat 必要が生じたときにコールできる方法これによりMailer 私たちのクラスとゆるく結びつくクラスHtmlFormatter and TextFormatter クラス.
    我々Mailer クラスは抽象化に依存しますIFormatter ) のHtmlFormatter and TextFormatter クラス.
    あなたの時間をありがとう.これは、このシリーズの終わりに私たちをもたらします.あなたが私のコンテンツを面白く見つけて、より知りたいならば、好きにしてください、そして、私に従ってください.
    私は、今後数週間でさまざまなトピックの記事の多くを投稿されるので、もしあなたがこのスペースにあなたの目を更新滞在して目を離さないしないようにしたくない.
    また、私のgithubに私に従うことができます.
    https://github.com/Caleb-Mantey/