誰に? まず第一に、おそらく私と同じ-ソフトウェアシステムの設計の分野の初心者に。 膨大な経験的経験がなく、一般的な考慮事項のみに基づいてデザインパターンを所有している場合。 このような記事をさらに効果的に読むことは、 SOLID 、 GRASP 、およびその他の設計原則を聞いたことがない人向けです。 論理の法則に基づいたこれらの基本的な理論的判断から、先験的に真実であると思われたすべての揺るぎない仮説がどのように導出されるかを示すことができることを心から願っています。
それにもかかわらず、そのような低い水準にもかかわらず、私は、経験豊富で経験豊富なプログラマーが、強力な経験的基盤を持ち、彼のニューラルネットワークにしっかりと座り、控えめな仕事に建設的なアドバイスを追加することを望みます。
まえがき
なぜ私はそのような道をたどり、そのような基本的で、おそらく長年理解されているトピックについて書いたのですか?
いくつかの理由。
第一に、私は常にリチャード・ファインマンに触発されました(これが最初でも最後でもないことを知っています)-好奇心の強い伝染性のハローと存在の非常に奥深くに浸透したい欲求を持つ最大の男。 無知になる前の彼の大胆不敵さはあなたを無関心のままにすることができないので、不確実性の深theに何度も挑戦したいと思うでしょう。
第二に、数学、特にそのアイデアと概念の多くが最小の公理に基づいて互いに由来しているという事実を賞賛することをやめません。 私は、人が基本的なルールに同意するとすぐに数学の世界全体がその存在を見つけるという見解を支持しています。彼にとって残っているのは、忍耐と投機の助けを借りて自分の労働の成果を刈り取ることです。
おそらく、結局のところ、最も基本的な動機は、基本的なデザインパターンと原則を適用する方法と場所を大まかに理解している日常の作業で、まだ、このコードがデザインの観点からどれだけ優れているかを定量的かつ正式に評価する深さ、能力に欠けていることです。 コードは芸術ではなく、分析できるのは厳密な構造であると心から確信しています。より強力で合理的なものを採用する機会が確かにある場合、「 美 」の反射感覚に焦点を当てることは効果的ではないと思います。
私は誰ですか? 私の名前はジョシュ、ハリコフ出身、22歳、そして今でもジュニアソフトウェア開発者です。 おそらく。 約1年前、私はすでにハブで公開されていましたが、その時点では、C#のコンポーネント指向エンジンのトピックに関する私の考えは、思ったほどひどく満たされていませんでした。 さらに、この出版物はサンドボックスから始まり、しばらくの間、意見が得られました。 しかし、これはそうです、私がすでに引きずった目的のための知人。
物語の構造について簡単に説明します。 この記事では、「 論理的・哲学的論文 」のルートヴィヒ・ヴィトゲンシュタインのように、論文を一貫性のあるものにすることを試みます。
作品の構造はいくつかの部分で構成されます。最初の部分は位置と概念のセットであり、場所によっては公理の形をとります。 この基盤は、理論が開発された地域の規則や法律として理解することができます。
第2部では、原則と法則(OOP、SOLID)がこれらの開発からどのように自然に続くかを示します。まるでそこに常に存在しているかのように、代数環の連想性と可換性は、常に存在しているように見えます。加算と乗算の概念にどのように同意するか。
さて、私は読者をそんなに長い序文で飽きさせないことを望みます、そしておそらく私たちは先に進むでしょう。
位置1.コードは人々によって書かれています。
注:この公理がそれほど昔ではないときにこの記事を読んでいる場合、まず、酔っているときに光の速度を超えないように報告してください。逆転すると、再び冷静になります。次に、この記事を閉じます。これは、ジョークのためだけに検索エンジンに表示されるためです。
構造
メタファー1 。 階層的にすべて。 宇宙は、クォーク、原子、分子、物質、細胞、組織など、さまざまな程度の近似レベルのセットと見なすことができます...下位レベルのモジュールが上位レベルのモジュールを加算すると、人間の思考自体が階層形式をとることは秘密ではありません。宇宙、またはそれにもかかわらず、私は哲学的な演習として好奇心reader盛な読者に任せます。そのような考えはプロットのメインラインに関係しないからです。
コンセプト1.1。 このタスクは、アプリケーション機能の要件です。
コンセプト1.2。 ブロックは、たった1つのタスクの実行を中心としたコードです。
コンセプト1.3。 依存関係 -別のコードのあるブロックで使用します。
コンセプト1.4。 近似の程度は、これを達成するためにアトミックから上昇する必要があるレベルの数です。
コンセプト1.5。 抽象化 -コンパイル段階で特定の実装を持たないブロック。
機能1.1。 Apr(x)は近似度xです。
機能1.2。 Qd(x)は、ブロックxの依存関係の数です。
規制1.1。 時間の経過とともに、ソフトウェアシステムを構成するブロックの数が増加します。
規制1.2。 ソフトウェアシステムのアトミックは、基本的な演算子とキーワードのレベルです。
規制1.3。 抽象化の近似度が高いほど、つまり 解決するために呼び出されるタスクがより一般的であるほど、変更が現れる可能性は低くなります。
規則1.3.1。 抽象化への依存は、間接的な変更につながる可能性が低くなります。
規則1.3.1.1。 抽象化はエントロピーを低下させます。
規制1.4。 冗長性は変化します。
プロセス
コンセプト2.1。 作成 -新しいコードを記述して、アプリケーションのブロック数を増やします。
コンセプト2.2。 変更 -ブロックへの問題の定式化の変更の表示。
コンセプト2.3。 間接的な変更 -ブロックの変更を依存するものにマッピングします。
コンセプト2.4。 正確性は、検証の定量的特性です。 高度な事前条件と事後条件に関して、ブロックがどの程度正確かつ完全に機能するかを示します。
コンセプト2.5。 エントロピーはコードの品質の定量的特性であり、新しい機能を導入するために必要な追加予算を示します。 これは、平均変更時間と平均ブロック作成時間の関係によって表されます。
機能2.1。 Tc(x、y)は、問題yのフレームワークにおけるブロックxの作成時間です。
機能2.2。 Tu(x、y)は、問題yのフレームワークにおけるブロックxの変更時間です。
機能2.3。 Qu(x、y) -問題yのフレームワークにおけるブロックxの変更数。
機能2.4。 Qm(x、y) -編組の数。 タスクyのフレームワークでブロックxの変更をブロックします。
機能2.5。 Md(x) -ブロックxの間接的な変更のセットから実際の変更につながる一連の変更へのマッピング。
機能2.6。 Cor(x) -ブロックxの正確さの度合いを示します。 理論的結果と実際の関係。 これは、期待される関数の結果と実際の結果との交差によって形成されるセット内の要素の数と、期待される関数の結果セット内の要素の数の比率として正式に定義できます。
機能2.7。 Ku(x)は、ブロックxの脆弱係数です。 Mdの数(x)と間接的な変化の数xの比率。
規制2.1。 新しいコードはエントロピーを増加させます。
規制2.2。 変更によりエントロピーが増加します。
規制2.3。 間接的な変更は、正確性を間接的に低下させます。
規制2.3.1。 間接的な変更は、間接的でないことにつながる可能性があります。
規制2.3.1.1。 間接的な変更は、エントロピーを間接的に増加させます。
規制2.3.2。 テストブロックは、正確性に対する間接的な変更の影響を軽減します。
組織
メタファー2.宇宙はなんとか神秘的に何も打ち負かさずに何かを創り出し、驚くほど単純なスキームに従って行動しました:彼女は存在の基本的な要素と相互作用する法則を決定しました、そして今私はそれについて書くために寒い冬の夜に座らなければなりません。
コンセプト3.1。 再利用 -同じユニットを使用して、アプリケーションのすべての場所で同じ問題を解決します。
コンセプト3.2。 ブロックポリモーフィズム -実装ブロックを抽象化に置き換える機能。
コンセプト3.3。 継承 -親構造の子ブロックによる再利用。
コンセプト3.4。 カプセル化 -ブロックの内部構造を、それを使用するブロックから隠します。
規制3.1。 再利用すると、変更の数が減り、間接的な変更の数が増えるため、エントロピーが減少し、その結果としてエントロピーが減少します。
規則3.2。 再利用するとコードの量が減ります。
規制3.3。 ポリモーフィズム、継承、およびカプセル化により、抽象化を使用できます。
規制3.4。 継承により再利用が増加します。
固い
最後に、最も一般的な原則-SOLIDの適用を理論的に実証する自由を取りたいです。
単一責任の原則 - 単一責任の原則 。 正式には、次のように聞こえます。ソフトウェアモジュールまたはクラスは、アプリケーションによって提供される1つの機能部分のみに対する責任を持つ必要があります。 彼には「 変化の唯一の理由 」があるはずです(ロバート・マーティン)。 理論モデルでは、これは依存関係の定義に直接従います:既に上で示したように、論理的に1つの最上位ブロックに異なるタスクを実行する2つの下位ブロックが含まれる状態ですが、相互に依存して、循環依存性のない状態よりもエントロピーが大きくなります。
オープン/クローズド原則 - オープンネス/クローズの原則 。 簡単に説明すると、エンティティの動作を変更するには、ソースコードを変更するのではなく、拡張する必要があります。つまり、継承、ポリモーフィズム、抽象化などの特定のメカニズムを意味します。 上記の構造に照らして、新しい機能を導入する時間は変更時間ではなく作成時間のみであると言えます。したがって、エントロピーが大幅に減少します。
リスコフ代替原理 -バーバラリスコフの代替原理。 これは、プログラムの正確性と操作性を損なうことなく、タイプTのすべてのオブジェクトをタイプSのオブジェクト( SはTのサブタイプ)に置き換えることが可能であるべきだと述べています。 公式言語では、これは次のように表現できます。関数f(x) を型Tのすべてのxに対して有効にし、関数f(y)も型Sのすべてのyに対して有効でなければなりません( SはTのサブタイプ) 。 この原則は、アプリケーションの脆弱性係数を減らしたいという願望の結果であり、残念ながら、特定の推奨事項を提示するのではなく、ソフトウェアシステムの要件を仮定するだけです。
インターフェース分離の原則 - インターフェースの分離の原理 。 多数の小さなインターフェースの方が1つの大きなインターフェースよりも優れていると発表します。 インターフェイス依存型クライアントは、必要な部分のみを使用できます。
この原則は、抽象化の分野に適用されるSRPの継続であることがわかります。 引数と証拠はまったく同じです。
依存関係の逆転の原則 - 依存関係の逆転の原則 。 私にとって、最も理解しにくい原則の1つは、高レベルのオブジェクトが低レベルのオブジェクトに依存するべきではなく、その逆も同じであるということです。両方のレベルは抽象化に依存する必要があります。 この原則の論理と真実は、 規則1.3.1.1から直接続きます 。抽象化の変更の確率は、特定のブロック実装者の変更の確率よりも低くなります。
悪名高いSOLIDの原則を考慮して、1つの目標を追求しました:依存関係の数を減らすか、ブロックが変更される可能性を減らすことによって、アプリケーションのエントロピーを減らすためのいくつかの戦略と方法の一般化と名前にすぎないことを示すことです。
これに関する理論を終わらせることを提案します。 読者の注意を引くために、完全に明白な情報がたくさん提供されました。 繰り返しますが、この部分は公理的根拠として提示されなければなりませんが、それでも、ほとんどの規定は、より低いレベルの公理、たとえば確率の加算の法則などに基づいて、適切な器用さで正式に証明できます
練習する
演習として、実際の例を検討し、その構造を定量的に分析してみます。
DamageMediatorは、損傷メディエーターのコンポーネントである最も単純なクラスです。 彼の仕事は、装備、武器、特性などを考慮してキャラクターのダメージを計算するような方法で、親コンテナのさまざまなコンポーネント間のトリッキーな相互作用を組み込むことです。
public class DamageMediator : GameComponent
{
public int Next()
{
var equipment = GetEquipment();
var stats = GetStats();
var weapon = equipment.Weapon;
var damage = weapon.Damage;
var isCrit = stats.CriticalChance.Next();
var result = isCrit ? damage.Next() + damage.Next() : damage.Next();
return result;
}
}
.
-: GetEquipment, GetStats, Equipment.Weapon, Weapon.Damage, Stats.CriticalChance, CriticalChance.Next, Damage.Next.
-: Equipment, Stats, Weapon, Damage, Chance.
Qd = 12.
, , .
1. GetEquipment GetStats, .
public class DamageMediator : GameComponent
{
public int Next(Equipment equipment, Stats stats)
{
var weapon = equipment.Weapon;
var damage = weapon.Damage;
var isCrit = stats.CriticalChance.Next();
var result = isCrit ? damage.Next() + damage.Next() : damage.Next();
return result;
}
}
2. Equipment Weapon.
public class DamageMediator : GameComponent
{
public int Next(Weapon weapon, Stats stats)
{
var damage = weapon.Damage;
var isCrit = stats.CriticalChance.Next();
var result = isCrit ? damage.Next() + damage.Next() : damage.Next();
return result;
}
}
3. Weapon Damage.
public class DamageMediator : GameComponent
{
public int Next(Damage damage, Stats stats)
{
var isCrit = stats.CriticalChance.Next();
var result = isCrit ? damage.Next() + damage.Next() : damage.Next();
return result;
}
}
4. Stats Chance.
public class DamageMediator : GameComponent
{
public int Next(Damage damage, Chance criticalChance)
{
var isCrit = criticalChance.Next();
return isCrit ? damage.Next() + damage.Next() : damage.Next();
}
}
: Qd = 4.
, , , , . , .
-, , , , : GetEquipment GetStats. — . , , : DamageMediator GameComponent GameComponentContainer ( , , ), , , .
-, . , ( , ) : , , , ?
, , , , , , , , , .
, , , , , , : , , . , .
, , , , : “ , , ? ”.
, DamageMediator: , (, , ), , , .
, , ( , ), .
, - . , , , , , — .
, , , .
, Next, . , . :
public class DamageMediator : GameComponent
{
// API.
public int Next()
{
// .
var equipment = GetEquipment();
var stats = GetStats();
return Next(equipment.Weapon.Damage, stats.CriticalChance);
}
private static int Next(Damage damage, Chance criticalChance)
{
var isCrit = criticalChance.Next();
return isCrit ? damage.Next() + damage.Next() : damage.Next();
}
}
, , ? , , : , . , , .. , , Qd = 4.
, , .
, .. , :
public class DamageMediator : GameComponent
{
public int Next()
{
var equipment = GetEquipment();
var stats = GetStats();
return DamageUtil.Next(equipment.Weapon.Damage, stats.CriticalChance);
}
}
, , , , . , , .
, , . , , , , .
, : , .. , ; , .. , ; , .. .
, , , , , .
, , , , , , , .. , , , .
, , - , , , , .
, , : , , , , , , .. , , . , , .. , , , , .
, , , , , — , , , , .
, . , , .
!