window.pipedriveLeadboosterConfig={です。 ベース:'leadbooster-chat.pipedrive.com'、 companyId:11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2、 } ;(function () { var w = window もし (w.LeadBooster) {なら console.warn('LeadBooster already exists') } else { w.LeadBooster = { {. q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: 関数 (n) { { this.q.push({ t: 'o', n: n, h: h }) this.q.push({ t: 't', n: n }) }, } } })() オープンクローズの原則。使う必要ある?- The Codest
The Codest
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 産業
    • フィンテック&バンキング
    • E-commerce
    • アドテック
    • ヘルステック
    • 製造業
    • 物流
    • 自動車
    • アイオーティー
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
戻る矢印 戻る
2019-07-02
ソフトウェア開発

オープンクローズの原則。使う必要があるのか?

マテウシュ・レスニアク

ほとんどの開発者は、ボブおじさんのSOLID原則の1つであるオープン-クローズの原則について聞いたことがあるだろう。理にかなっているように聞こえるが、「生きた」コードで最初に使うまでは、まだ少しぼやけていることがある。原則の全容は次のとおりだ:ソフトウェアの実体(クラス、モジュール、関数など)は、拡張に対してはオープンであるべきだが、変更に対してはクローズであるべきだ。

では、その本当の意味は?

私たちは、オープン・クローズの原則の本当の意味を教えてくれた開発問題に遭遇しました。あるウェブ・アプリケーションの中に、2つのセクションを持つフォームがありました:

  • 需要チャンネル
  • ダイナミック・フィルター

ユーザーは好きなだけフィルターを追加することができるが、いくつかのルールがある。

需要チャンネルAD交換、ヘッダー入札、予約、その他 動的フィルター(次元):ウェブサイト、広告ユニット、ジオ、クリエイティブサイズ、デバイス

この記事は主にコード・リファクタリングに関するものなので、以下にコード・スニペットがたくさん出てくる。なるべく少なくするように努めたつもりだが、以下のようなコードを示すにはある程度の量が必要だ。 コード・リファクタリング.主旨を理解するためにコードの細かい部分まで理解する必要はない。

最初の実装はシンプルだった:

クラスResearchFormStateUpdater { {}の
  更新 () {
    (...)
    this._updateDynamicFilters();
  }

  _updateDynamicFilters(){を更新します。
    $('.dynamic-filter').each((_、filter) => {。
      $(filter).trigger('dynamicFilter:disableWebsites', this._shouldDisableWebsitesFields());
    });
  }

  _shouldDisableWebsitesFields(){を返します。
    return this._shouldDisableFields(ResearchFormStateUpdater.WEBSITE_DISABLING_DEMAND_CHANNELS);
  }

  _shouldDisableFields(disablingDemandChannels){」を返します。
    // disablingDemandChannels のいずれかがチェックされているか?
  }
}

ResearchFormStateUpdater.WEBSITE_DISABLING_DEMAND_CHANNELS = ['header_bidding', 'reservation', 'other'];

クラスResearchDynamicFilter
  _setDynamicFilterDisableWebsitesEvent(){を設定します。
    $(this._getBody()).on('dynamicFilter:disableWebsites', (event, shouldDisableWebsites) => {)
      // ウェブサイト・フィルタを無効にする
    });
  }
}

ご覧のように、ウェブサイト・フィルターはHEADERでは利用できないことになっている。BIDDING、RESERVATION、OTHERの各チャンネルはADのみ視聴可能です。EXCHANGEチャンネル。

コードについて最後に言えることは、それは永久的なもの、あるいは静的なものだということだ。そのため、クライアントからの要求が増え、これらのクラスはより大きく、より複雑になっている。

機能開発

  • 別のチャンネルを追加 - EBDA (EBDAが選択されている間、ウェブサイトフィルターは利用できないはずである):

    • 拡大する disabling_demand_channels。 EBDA需要チャネル別
    • 最初の実装では、メソッド名と定数名にウェブサイトを指定した。例えば

      • isWebsitesDimensionDisabled へのareFormStateDimensionsDisabled
      • ウェブサイト・デマンド・チャンネルの無効化 への disabling_demand_channels。

ネタバレ注意 -> コンポーネントが変更のためにオープンになっている場合、将来的に多くの名前が変更されるでしょう。次のステップでは、このことには注意を払わない。

  • 製品'に別のフィルターを追加する (製品 フィルタの可用性スキームはウェブサイトと同じ)

    • リサーチダイナミックフィルター クラスは、フィールドを無効化/有効化する間に、もう1つの次元をチェックしなければならない。
  • もっと大きくして、チャンネル→「ソース」の上にスイッチャーを追加しよう。 今まで持っていたデマンドチャンネルはすべて広告マネージャのソースにある。新しいソース(SSP)にはデマンドチャンネルがなく、利用可能なフィルターはウェブサイトだけです。

    • ルール

      • ソースには2つの状態がある:広告マネージャ、SSP。
      • すべてのデマンドチャネルは、Ad Managerソースでのみ利用可能です。
      • SSPのソースには需要チャンネルがない
      • ウェブサイト」は、SSPソースで利用可能な唯一のフィルターである。
    • 実施する:

      • SSP』を選択した場合:

        • デマンドチャンネルを無効にする。
        • トリガー 'dynamicFilter:disableWebsitesAndProducts' <- 両方を有効にする
        • トリガー 'dynamicFilter:disableNonSspOptions'。
      • 広告マネージャがチェックしたとき:

        • トリガー 'dynamicFilter:disableWebsitesAndProducts' <- 天候の有効無効をチェックする
  • プラットフォーム」に別のフィルターを追加する

    • ルール

      • プラットフォームは、ソースがSSPの場合のみ利用可能。
    • 難易度が高い:

      • 現在、広告マネージャとSspのAD_EXCHANGEチャネルで利用可能な「ウェブサイト」と、Sspでは利用可能だが広告マネージャでは利用できない「プラットフォーム」があります。
      • トグリング フォームの状態は実に厄介で混乱しやすい。

新機能の実装:

次のスニペットは、主にコードの複雑さを示すためのものです。自由に隠しておいてください。

クラスResearchFormStateUpdater
  更新 () {
    (...)
    this._triggerCallbacks();
  }

  _triggerCallbacks(){。
    // ソースに応じてコールバックを選択
  }

  _adManagerSourceCallbacks(){は、次のとおりです。
    (...)
    this._enableDemandChannels(ResearchFormStateUpdater.AD_MANAGER_DEMAND_CHANNELS);
    this._updateDefaultStateOfDynamicFilters();
    this._updateAdManagerDynamicFilters();
  }

  _sspSourceCallbacks(){次のようになります。
    (...)
    this._removeDemandChannelsActiveClassAndDisable(ResearchFormStateUpdater.AD_MANAGER_DEMAND_CHANNELS);
    this._updateDefaultStateOfDynamicFilters();
  }

  _updateDefaultStateOfDynamicFilters(){。
    $('.dynamic-filter').each((_、filter) => {。
      $(filter).trigger('dynamicFilter:enableSspFilters', this.isSourceSsp);
    });
  }

  _updateAdManagerDynamicFilters(){。
    $('.dynamic-filter').each((_、filter) => {。
      $(filter).trigger('dynamicFilter:disableWebsitesAndProducts', this._areFormStateDimensionsDisabled() && !this.isSourceSsp);
    });
  }

  _shouldDisableFields(disablingDemandChannels){。
    // disablingDemandChannelsのいずれかがチェックされた場合
  }
}

ResearchFormStateUpdater.AD_MANAGER_DISABLING_DEMAND_CHANNELS = ['header_bidding', 'reservation', 'other', 'ebda'];

クラス ResearchDynamicFilter
  // 現在の実装の複雑さを示すために、この2つのメソッドは単純化していない。

  _setDefaultDynamicFiltersToggleEvent(){を設定します。
    $(this._getBody()).on('dynamicFilter:enableSspFilters',(イベント, shouldEnableSspOptions) => {
      this._setDefaultFiltersOptionDisabledState(shouldEnableSspOptions);

      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val();
      if (selectedFilterDimension === 'website') { 以下のようになります。
        this._toggleChosenFilterDisabledState(false);
      もし (selectedFilterDimension === 'platform') { if
        this._toggleChosenFilterDisabledState(!shouldEnableSspOptions);
      } else {
        this._toggleChosenFilterDisabledState(shouldEnableSspOptions);
      }
    });
  }

  _setDynamicFilterDisableWebsitesAndProductsEvent(){次のようになります。
    $(this._getBody()).on('dynamicFilter:disableWebsitesAndProducts', (event, shouldDisableWebsitesAndProducts) => {)
      const selectedFilterDimension = this._getFiltersDimension().find('option:selected').val();
      if ($.inArray(selectedFilterDimension, ['website', 'product']) >= 0) { { ($.inArray(selectedFilterDimension, ['website', 'product']) >= 0)
        this._toggleChosenFilterDisabledState(shouldDisableWebsitesAndProducts);
      }
      this._setMethodSelectWebsiteAndProductOptionDisabledState(shouldDisableWebsitesAndProducts);
    });
  }

  を設定します。
    $.each(ResearchDynamicFilter.NON_SSP_FILTERS_OPTIONS,(_、option) => { // 'SSP'に応じてフィルタの状態をトグルする。
      // 'shouldDisable'に応じてフィルタの状態をトグルする。
    });
  }
}

ResearchDynamicFilter.NON_SSP_FILTERS_OPTIONS = ['ad_unit', 'creative_size', 'geo', 'device', 'product'];

今でもいくつか使っている。 トグル のメカニズムです。4つのレバーを切り替えて期待通りの状態にするのは本当に難しいし、今やDynamicFilterは、どの寸法がsspソース用でないかを知る必要がある。

私たちにはResearchFormStateUpdaterがある。

最終リクエスト

イールド・パートナー」に別のフィルターを追加する

私たちがこれらのクラスのリファクタリングを決めたのは、まさにその瞬間だった。分析されるチャンネルとフィルターは問題のほんの一部に過ぎない。ここには複数のフォームセクションがあり、そのすべてに同じ問題があります。私たちのリファクタリングによって、これらのクラスのメソッド内部を変更する必要性がなくなります。

次のスニペットでは、メイン・クラスがどれだけわかりやすくなったかをお見せするために、本番のコードとほとんど同じにしておきました。

クラスResearchFormStateUpdater { {}の
  更新 () {
    (...)
    this._updateDynamicFilters();
  }

  _updateDynamicFilters(){。
    this._toggleAllDynamicFiltersState(this._dynamicFiltersDimensionsToBeDisabled());
  }

  _dynamicFiltersDimensionsToBeDisabled () { { { { _dynamicFiltersDimensionsToBeDisabled()
    if (this.isSourceSsp) { return ResearchFormStateUpdater.NO_SSP_FILTERS; }.

    var disabledFilters = ResearchFormStateUpdater.ONLY_SSP_FILTERS;
    if (this.areDemandChannelsExceptAdxSelected) { 以下のようになります。
      disabledFilters = disabledFilters.concat(ResearchFormStateUpdater.ONLY_ADX_FILTERS);
    }
    return disabledFilters;
  }

  _toggleAllDynamicFiltersState(disabledFilters){。
    $('.dynamic-filter').each((_、filter) => {。
      this._toggleDynamicFilterState(filter, disabledFilters);
    });
  }

  _toggleDynamicFilterState (dynamicFilter, disabledFilters) { $(dynamicFilter, disabledFilters)
    $(dynamicFilter).trigger('dynamicFilter:toggleDynamicFilters', disabledFilters);
  }
}

ResearchFormStateUpdater.NO_SSP_FILTERS = ['ad_unit', 'creative_size', 'geo', 'device', 'product'];

ResearchFormStateUpdater.ONLY_SSP_FILTERS = ['platform'];

ResearchFormStateUpdater.ONLY_ADX_FILTERS = ['website', 'product'];

クラスResearchDynamicFilter
  _setDynamicFiltersToggleEvent(){を設定します。
    $(this._getBody()).on('dynamicFilter:toggleDynamicFilters',(イベント、disabledFilters) => {)
      this._disableFilters(disabledFilters.split(','));
      this._enableFilters(disabledFilters.split(',')));
    });
  }

  _disableFilters (filtersToDisable) { // filtersToDisableを無効にする。
    // filtersToDisableを無効にする
  }

  _enableFilters (filtersToDisable) { // filtersToDisable を無効にする。
    const filtersToEnable = $(ResearchDynamicFilter.ALL_FILTERS).not(filtersToDisable).get();
    // filtersToEnable を有効にします。
  }
}

ResearchDynamicFilter.ALL_FILTERS = ['website', 'ad_unit', 'creative_size', 'geo', 'device', 'product', 'platform'];

やったよ!やったか?

これで、'ResearchDynamicFilter'が知る必要があるのは、すべてのフィルターのリストだけになった。残りのロジックと制御は、上位のメソッドと定数から来る。

それでは、「Yield_partner」のフィルターを追加して、新しい構造を試してみましょう:

クラスResearchFormStateUpdater
  _dinamicFiltersDimensionsToBeDisabled(){」を参照してください。
    (...)
    if (this.areDemandChannelsExceptEbdaSelected) { disabledFilters = disabledFilters.concat(ResearchFormStateStateUpater) { (...)
      disabledFilters = disabledFilters.concat(ResearchFormStateUpdater.ONLY_EBDA_FILTERS);
    }
    return disabledFilters;
  }
}

ResearchFormStateUpdater.NO_SSP_FILTERS = [(...), 'yield_partner'];

ResearchFormStateUpdater.ONLY_EBDA_FILTERS = [(...), 'yield_partner'];

ResearchDynamicFilter.ALL_FILTERS = [(...), 'yield_partner'];

おわかりのように、定数にいくつかの値を追加し、いくつかの条件を追加するだけである。

オープン・クローズの原則」のおかげで、フォームのビジネス・ロジックを、より抽象度の高い値や条件を追加するだけで変更することができる。コンポーネントの中に入って何かを変更する必要はない。このリファクタリングはフォーム全体に影響し、さらに多くのセクションができましたが、現在ではすべてオープンクローズの原則に従っています。

私たちはコード量を減らしてはいない。むしろ増やしている(before/after):

  • ResearchFormStateUpdater - 211/282行
  • リサーチダイナミックフィルター - 267/256行

定数のコレクションがすべてなのだ。それは今や私たちのパブリック・インターフェースであり、何十ものスイッチャーなしでプロセスをコントロールするためのコンソールなのだ。

あわせて読みたい:

  • 良質なコードを書くには?
  • Vuelendar。Vue.jsをベースにしたCodestの新しいプロジェクト。
  • Ruby on Jetsとは何か、それを使ってどのようにアプリを作るのか?

関連記事

ソフトウェア開発

将来を見据えたウェブ・アプリケーションの構築:The Codestのエキスパート・チームによる洞察

The Codestが、最先端技術を駆使してスケーラブルでインタラクティブなウェブアプリケーションを作成し、あらゆるプラットフォームでシームレスなユーザー体験を提供することにどのように秀でているかをご覧ください。The Codestの専門知識がどのようにデジタルトランスフォーメーションとビジネス...

ザ・コデスト
ソフトウェア開発

ラトビアを拠点とするソフトウェア開発企業トップ10社

ラトビアのトップソフトウェア開発企業とその革新的なソリューションについて、最新記事でご紹介します。ラトビアの技術リーダーたちがあなたのビジネスをどのように向上させるかをご覧ください。

thecodest
エンタープライズ&スケールアップ・ソリューション

Javaソフトウェア開発の要点:アウトソーシングを成功させるためのガイド

outsourcingのJavaソフトウェア開発を成功させるために不可欠なこのガイドを読んで、The Codestで効率性を高め、専門知識にアクセスし、プロジェクトを成功に導きましょう。

thecodest
ソフトウェア開発

ポーランドにおけるアウトソーシングの究極ガイド

ポーランドのoutsourcingの急増は、経済、教育、技術の進歩がITの成長とビジネス・フレンドリーな環境を促進していることによる。

ザ・コデスト
エンタープライズ&スケールアップ・ソリューション

IT監査ツール&テクニック完全ガイド

IT監査は、安全かつ効率的で、コンプライアンスに準拠したシステムを保証します。その重要性については、記事全文をお読みください。

The Codest
ヤクブ・ヤクボヴィッチ CTO & 共同創設者

ナレッジベースを購読して、IT部門の専門知識を常に最新の状態に保ちましょう。

    会社概要

    The Codest - ポーランドに技術拠点を持つ国際的なソフトウェア開発会社。

    イギリス - 本社

    • オフィス 303B, 182-184 High Street North E6 2JA
      イギリス、ロンドン

    ポーランド - ローカル・テック・ハブ

    • ファブリチュナ・オフィスパーク、アレハ
      ポコジュ18、31-564クラクフ
    • ブレイン・エンバシー, コンストルクトースカ
      11, 02-673 Warsaw, Poland

      The Codest

    • ホーム
    • 会社概要
    • サービス
    • Case Studies
    • ノウハウ
    • 採用情報
    • 辞書

      サービス

    • アドバイザリー
    • ソフトウェア開発
    • バックエンド開発
    • フロントエンド開発
    • Staff Augmentation
    • バックエンド開発者
    • クラウドエンジニア
    • データエンジニア
    • その他
    • QAエンジニア

      リソース

    • 外部ソフトウェア開発パートナーとの協力に関する事実と神話
    • 米国から欧州へ:アメリカの新興企業がヨーロッパへの移転を決断する理由
    • テックオフショア開発ハブの比較:テックオフショア ヨーロッパ(ポーランド)、ASEAN(フィリピン)、ユーラシア(トルコ)
    • CTOとCIOの課題は?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • ウェブサイト利用規約

    著作権 © 2025 by The Codest。無断複写・転載を禁じます。

    jaJapanese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek jaJapanese