コードの共通化を原則とするのはアンチプラクティス 〜 現代のプログラミング原則

(Last Updated On: 2020年1月27日)

同じ処理を行うコードの共通化は、基本的には、ベストプラクティスです。しかし、アンチプラクティスとなる場合もあります。

コードの共通化(モジュール化)が原則とされた時代もあった

コードの共通化(モジュール化)とは、同じ/同類の処理を関数などに定義し、同じ処理を繰り返し書かない様にすることです。

先に書いた通り、コードの共通化、は基本的にはベストプラクティスです。このため、大昔は”プログラミングの基本原則”として扱われていた時代もありました。しかし、現代では”プログラミングの基本原則”とは考えられていません。

コードの共通化(モジュール化)とは、同じ/同類の処理を関数などに定義し、同じ処理を繰り返し書かない様にすることは当然の事で絶対にしなければならないことでは?なぜアンチプラクティスになるのか?と思ったかも知れません。

TL;DR;

現在はOO設計/開発をしている方ばかりでしょう。カプセル化とコードの共通化は共存できません。カプセル化するならコードは共通化の反対に、最低限でも異なるカプセル化が必要となる部分でコードの共通化を行わないようにします。(カプセル化の意味が無くなり、無駄に依存性を高める。依存性排除の為に非OOでもコードを共通化しないことも多い)

コードの共通化は基本的にはベストプラクティスですが、”原則にする”のはアンチプラクティスです。 ※ 原則: 全てのケースに適用すべきルール(例外は極一部)、ベストプラクティス: 大抵の場合はそうした方が良い結果を生む指針(例外が多い)

現代のプログラミング原則の一つはカプセル化

オブジェクト指向設計をしている方は”カプセル化”を常に意識していると思います。Wikipediaではカプセル化は以下のように解説されています。

カプセル化(カプセルか、encapsulation)とは、オブジェクト指向を構成する概念の一つ。オブジェクト内部のデータを隠蔽したり、オブジェクトの振る舞いを隠蔽したり、オブジェクトの実際の型を隠蔽したりすることをいう。「カプセル化=データ隠蔽」と勘違いされやすいが、データ隠蔽はカプセル化の具体例の1つにすぎず、同一のものではない。

適切なカプセル化を行う場合、コードの共通化(モジュール化)を行うと問題になる場合があります。オブジェクト指向設計で過度なコードの共通化を行うと、オブジェクトが密結合した状態になってしまいます。オブジェクトが密結合した状態になると、コードのメンテナンスや拡張が困難になります。この為、できる限りオブジェクトが疎結合(密結合しない状態)となるように設計します。その代表的なデザインパターンが依存性注入(DI)です。

依存性注入(DI)を利用すると、ベースクラスの共通化コードを使わなくて済み、密結合状態になることを防げます。DIのように密結合状態にならないようにする手法は多数あり、これらの手法はベストプラクティスとされています。

コードの共通化(モジュール化)が原則であるとされた時代もありましたが、現代では原則ではなく単なるベストプラクティスです。

コードの共通化(モジュール化)を原則とする弊害

原則とは、一部の例外を除き、常に適用/採用すべき手法/ルールです。例外は極一部でなければなりません。例外ばかりの原則となる場合は原則とせず「大抵の場合はこうやった方が良いベストプラクティス」として扱わないと余計な混乱や問題を生みます。

手続き型のプログラミングでも、コードの共通化(モジュール化)を原則として、共通化できそうなコードを手当たり次第に共通化すると、コード間におかしな依存性が発生してメンテナンスが困難になります。盲目的に「コードが共通化できるか?」だけではなく「適切にコードが分離され、おかしな依存性がないか?」も考えながらコーディングする必要があります。

拡張性の高いオブジェクト指向コードを書くためには、コードの共通化(モジュール化)を原則、には出来ません。原則としてしまうと密結合の状態を作ってしまい、メンテナンスが困難な一枚岩のソフトウェアになってしまいます。現代のオブジェクト指向設計では疎結合になるよう設計するのが当たり前で、コードの共通化(モジュール化)を原則、とすることは出来ません。

コードの共通化(モジュール化)による問題はセキュアコーディング原則である「入力をバリデーションする」でも発生します。入力データバリデーションのアーキテクチャーは次の2つに大別できます。

  • 分散型 – ソフトウェアの信頼境界(入力データが入ってくる部分)でバリデーション
  • 集中型 – ソフトウェア内部(MVCのモデルなど)で集中してバリデーション

アプリケーションの要求仕様が適切であるなら、分散型と集中型のどちらを使っても、両方を使っても構いません。(基本的には両方使うのがベストプラクティスです)

しかし、データバリデーションを行う場合に必ず考慮しなければならない原則があります。

  • フェイルファースト – 失敗するモノはできる限り早く失敗させるリスク管理の基本原則
  • ゼロトラスト – 検証/保証なしに全てのモノを信頼しないリスク管理の基本原則

これらの原則を考慮すると、原則的に「データバリデーションには分散型を採用する」ことになります。コードの共通化(モジュール化)が原則だ、と思っているとこれらの「原則とに反した集中型のデータバリデーション」をアーキテクチャーとして採用してしまうことに繋がります。

セキュリティ要件が重要なソフトウェアでは分散型と集中型の両方を利用する方が良いのですが、コードの共通化(モジュール化)が原則だ、と思っていると脆弱な集中型のみのバリデーションアーキテクチャーで済ませてしまいます。そして、入力バリデーション漏れ(未検証入力)が在るにも関わらず見逃す事になりがち、というよりも見逃しているモノがほとんどです。

セキュリティから見たコードの共通化

セキュリティ対策の基本原則に「多層防御」があります。多層防御を行うと、どうしても似たようなコード/同じコードが複数箇所に必要になることがあります。これをまとめて集中/共有化させてしまうのは、必ずしもベストプラクティスではありません。

集中と分散は適材適所

プログラムに限らず、何でも集中的にやればよい、というものではありません。集中的にやるのが絶対条件ではないです。分散させた方がやりやすいなら分散、組み合わせてやった方がよいなら組み合わせる。集中型と分散型は適材適所で使うもの、集中型は原則ではない、とコードの共通化は考えておいた方が設計に幅を持たせ、適切な設計を行いやすくします。

原則/ベストプラクティス、と言われているモノでも場合によってはアンチプラクティスになる、といったケースは少なくありません。原則/ベストプラクティスだから、ではなく「本当に最適なのか?」と常に疑うくらいの方が良いと思います。

中には”ベストプラクティス”と言われているものが、本当は”アンチプラクティス”だった、というケースも少くありません。

参考

例えばデータバリデーション一つにしても、適材適所でバリデーションすると3箇所に分散されます。これを一つにまとめようとしても良いことは何もありません。

投稿者: yohgaki