コードは思考です。 問題が発生し、開発者はそれを解決する方法、関数とクラスで要件を表現する方法、それらを友達にする方法、厳密さと正確さを達成する方法、ビジネスを復avenする方法を考えます。 実践、技術、原則、パターン、アプローチ-すべてを考慮する必要があり、すべてを覚えておく必要があります。
これに伴い、マネージャー、ヘルパー、サービス、コントローラー、セレクター、アダプター、ゲッター、セッター、その他の悪霊が広まっています。これはすべてデッドコードです。 彼は足etterと雑然とした。
このように戦うことを提案します。プログラムを自然言語のテキストとして提示し、それに応じて評価する必要があります。 これとどうなるかは記事にあります。
サイクルの目次
- オブジェクト
- アクションとプロパティ
- テキストとしてコード
プロローグ
私の経験はささやかなものです(約4年)が、仕事をすればするほど理解が深まります。プログラムが読めない場合は意味がありません。 それは長い間知られており、思い出させるためにbeatられました-コードは現在いくつかの問題を解決するだけでなく、 後でも:それはサポートされ、拡張され、修正されました。 さらに、彼は常に:読んでください。
結局のところ、これはテキストです。
テキストとしてのコードの美学は、サイクルの重要なテーマです。 ここの美学とは、物事を見て、いい、いい、美しい、と言うグラスです。
美と理解の問題では、言葉は非常に重要です。 「現時点では、血中のエタノール濃度が高いために私の知覚は鈍い状態にあります」は、「 酔っぱらった」とはまったく異なります。
私たちは幸運なプログラムであり、ほぼ完全に言葉で構成されています。
「健康とマナを持ち、歩く、攻撃する、呪文を使用するキャラクター」を作成する必要があるとしましょう。すぐに見ることができます: オブジェクト (キャラクター、健康、マナ、呪文)、 アクション (歩く、攻撃、使用)およびプロパティ (キャラクターの体力、マナ、呪文のspeed唱速度など)-これらはすべて名前になります:クラス、関数、メソッド、変数、プロパティ、フィールド、一言で言えば、プログラミング言語が分割するすべて。
しかし、クラスを構造から区別し、フィールドをプロパティから、メソッドを関数から区別しません:物語の一部としての文字は、技術的な詳細(参照または重要なタイプのいずれかで表現できる)に依存しません。 本質的に異なるもの:これはキャラクターであり、彼らは彼をHero (またはCharacter )と呼び、 HeroDataまたはHeroUtilsはないHeroData HeroUtils 。
さて、私はまさに美学のグラスを取り上げて、今日のコードがどのように書かれているのか、なぜ完璧からはほど遠いのかを示します。
オブジェクト
C#では(だけでなく)、オブジェクト-ヒープに配置され、しばらくそこに存在し、その後ガベージコレクターがそれらを削除するクラスのインスタンス。 スタックまたは連想配列、またはその他の構造を作成することもできます。 私たちにとっては、クラス名、名詞です。
コード内の名前は、一般的な名前と同様、混乱を招く可能性があります。 はい、見苦しい名前はめったに見ませんが、美しいオブジェクトです。 特にManager場合。
オブジェクトの代わりにマネージャー
UserService 、 AccountManager 、 DamageUtils 、 MathHelper 、 GraphicsManager 、 GameManager 、 VectorUtil 。
ここで支配するのは正確性と具体性ではなく、霧の中にどこかに残る曖昧なものです。 そのような名前の場合、多くは許容されます。
たとえば、 GameManagerでは、 ゲームおよびゲームロジックに関連するものを追加できます。 6か月後、技術的な特異点があります。
または、Facebookで作業する必要があります。 FacebookManagerまたはFacebookServiceすべてのコードを1か所に配置してみませんか? それは誘惑的に単純に聞こえますが、そのようなあいまいな意図は、同様にあいまいなソリューションを作成します 。 同時に、Facebookにはユーザー、友人、メッセージ、グループ、音楽、興味などがあることがわかっています。 十分な言葉!
十分な言葉があるだけでなく、私たちはまだそれらを使用しています。 プログラム間ではなく、通常のスピーチでのみ。
そして、それはGitUtilsではなく、 IRepository 、 ICommit 、 IBranchです。 ExcelHelperではなく、 ExcelDocument 、 ExcelSheet 。 GoogleDocsServiceではなく、 GoogleDocsです。
すべてのサブジェクト領域はオブジェクトで満たされています。 「オブジェクトは巨大なボイドによってマークされていました」 、 「心臓は猛烈に鼓動していました」 、 「家は立っていました」 -オブジェクトは機能し、感じ、想像しやすいです。 彼らはここのどこかに、有形で密集しています。
同時に、あなたは時々これを見ます: Microsoft/calculatorリポジトリ-メソッドを持つCalculatorManager : SetPrimaryDisplay 、 MaxDigitsReached 、 SetParentDisplayText 、 OnHistoryItemAdded ...
(それでも、 UtilsManager 見たことが UtilsManager ます...)
このようになります。新しい動作でList<>タイプを拡張したいのですが、 ListUtilsまたはListHelperが生まれました。 この場合、拡張メソッドListExtensions のみを使用する方がより適切で正確ListExtensions 。これらは概念の一部であり、プロシージャからのダンプではありません。
数少ない例外の1つは、投稿としてのOfficeManagerです。
それ以外の場合...プログラムにそのような単語が含まれている場合は、コンパイルしないでください。
オブジェクトの代わりのアクション
IProcessor 、 ILoader 、 ISelector 、 IFilter 、 IProvider 、 ISetter 、 ICreator 、 IOpener 、 IHandler ; IEnableable 、 IInitializable 、 IUpdatable 、 ICloneable 、 IDrawable 、 ILoadable 、 IOpenable 、 ISettable 、 IConvertible 。
ここで、本質の本質は概念ではなく手順であり、コードは再びイメージと読みやすさを失い、通常の言葉は人工的な言葉に置き換えられます。
IEnumerableではなく、より活気のあるものはISequenceです。 IBlueprintではなくICreator 。 IButtonではありませIButtonPainter 。 IFilterではなくIPredicate 。 IGateではなくIOpeneable 。 IToggleではなく、 IEnableable 。
良い話は、キャラクターがどのように作成し、ビルダーが構築し、ペインターが描くかではなく、キャラクターとその開発について語っています。 アクションはオブジェクトを完全に表すことはできません。 ListSorterはListSorterはありません。
たとえば、ファイルシステム上のフォルダーをクリーンアップするオブジェクトであるDirectoryCleanerを取り上げます。 エレガントですか? しかし、「フォルダークリーナーにD:/ Testをクリーンアップするように依頼する」 、常に「D:/ Testにクリーンアップする」とは決して言わないので、 Cleanメソッドを使用したDirectoryはより自然に近くなります。
より活気のあるケースは、より興味深いものですFileSystemWatcherは、変更を報告するファイルシステムオブザーバーです。 しかし、なぜ変化そのものが起こったと報告できるのであれば、なぜオブザーバー全体なのでしょうか? さらに、これらはファイルまたはフォルダーと密接にリンクしている必要があるため、 DirectoryまたはFile配置する必要がありFile ( Changesプロパティを使用してfile.Changes.OnNext(action)を呼び出すことができます)。
このような口頭の名前は、 「アルゴリズムのファミリをカプセル化する」ように指示するStrategy設計パターンを正当化するようです。 しかし、 「アルゴリズムのファミリ」ではなく、ストーリーに存在する本物のオブジェクトを見つけた場合、戦略は単なる一般化であることがわかります。
これらおよび他の多くのエラーを説明するために、哲学に目を向けます。
存在は本質に先行する
MethodInfo 、 ItemData 、 AttackOutput 、 CreationStrategy 、 StringBuilder 、 SomethingWrapper 、 LogBehaviour
そのような名前は一つのことで結ばれています:それらの存在は詳細に基づいています。
何かがタスクにすぐに干渉することが起こります。何かが欠落しているか、または存在していますが、何かではありません。 次に、 「Xを行う方法を知っているものが今私を助けるだろう」と考えます。これが存在の構想です。 次に、Xを「実行」するために、 XImplが書き込まれます。これがエンティティの表示方法です。
したがって、 IArrayItem代わりに、 IIndexedItemまたはIItemWithIndexがより一般的です。たとえば、Reflection APIでは、 メソッド ( Method )の代わりに、それに関する情報 ( MethodInfo )のみが表示されます。
もっと真の方法:存在の必要性に直面し、それを実装するエンティティを見つけ 、そしてこれがその性質であるため、他のエンティティ。
たとえば、中間インスタンスを作成せずにstring型の値を変更したいと考えていました。これはStringBuilderの形式の簡単なソリューションであることが判明しましたが、私の意見では、より適切MutableString 。
ファイルシステムを思い出してください: DirectoryRenamerフォルダーの名前を変更する必要DirectoryRenamerません。Directoryオブジェクトの存在に同意するとすぐにアクションがその中にあるため、コード内で対応するメソッドが見つからないためです。
ロックの取得方法を説明する場合、 ILockBehaviourまたはILockStrategyを指定する必要はありませんILockBehaviourまたはILockStrategy方がはるかに簡単ですILock ( IDisposableを返すAcquireメソッドを使用)またはICriticalSection ( Enter )。
ここで、すべての種類のData 、 Info 、 Output 、 Input 、 Args 、 Params (あまり頻繁にはState )は、一方的なものと見なされたため、動作がまったくないオブジェクトです。
存在が主である場合、商は共通のものと混同され、オブジェクトの名前は紛らわしいです-各行を読み、キャラクターがどこに行ったのか、なぜ彼のDataだけがあるのかを把握する必要があります。
奇妙な分類
CalculatorImpl 、 AbstractHero 、 ConcreteThing 、 CharacterBase 。
上記と同じ理由で、階層内の場所が正確に示されているオブジェクトが表示される場合があります。 繰り返しますが、存在は急いでいます。また、結果を考慮せずに、差し迫った必要性がコードに急いでこぼれたことがわかります。
結局のところ、実際に人( Human )がいますか?ベースの人( HumanBase )の相続人ですか? しかし、 Item AbstractItem継承する場合はどうですか?
時々、 Characterではなく、ある種の「生の」類似性-CharacterRawがあることを示したいことがあります。
Impl 、 Abstract 、 Custom 、 Base 、 Concrete 、 Internal 、Raw-不安定性の兆候、建築の曖昧さ。これは、最初のシーンの銃のように、後で確実に撃ちます。
繰り返し
ネストされたタイプでは、これは起こりRepositoryItem : RepositoryItem Repository 、 Window WindowState 、 Hero HeroBuilder 。
繰り返しは意味をカットし、欠陥を悪化させ、テキストの複雑さのみに寄与します。
冗長部品
スレッドを同期するには、次のAPIでManualResetEventがよく使用されます。
public class ManualResetEvent {
個人的には、 SetがReset (不快な文法)とどのように異なるか、およびストリームを操作する場合の「手動でリセットするイベント」とは何であるかを常に覚えておく必要があります。
そのような場合、プログラミングから遠く離れたメタファーを使用する方が簡単です(ただし、日常生活に近い):
public class ThreadGate { void Open(); void Close(); bool WaitForOpen(); }
混乱することは確かにありません!
時々ばかげたことになる: オブジェクトは単なるItemsではなく、必然的にItemsListまたはItemsDictionaryあることを明確にしている!
ただし、 ItemsList面白くない場合は、 Springの AbstractInterceptorDrivenBeanDefinitionDecorator ItemsListません。 この名前の単語は、巨大なモンスターが縫い付けられるぼろです。 しかし...これがモンスターである場合、次に何をHasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitorますかHasThisTypePatternTriedToSneakInSomeGenericOrParameterizedTypePatternMatchingStuffAnywhereVisitor ? 遺産を願っています 。
クラス名とインターフェイス名に加えて、変数またはフィールドの冗長性にしばしば遭遇します。
たとえば、タイプIOrdersRepositoryフィールドはIOrdersRepositoryと呼ばれ_ordersRepository 。 しかし、注文がリポジトリによって送信されたことを報告することはどれほど重要ですか? 結局のところ、はるかに簡単です_orders 。
また、LINQクエリでは、たとえばPlayer.Items.Where(item => item.IsWeapon)などのラムダ式の完全な引数名を書き込みますが、 Player.Items.Where(item => item.IsWeapon)を見ることでこの項目を既に理解していPlayer.Items 。 そのような場合、私は常に同じ文字x : Player.Items.Where(x => x.IsWeapon)を使用するのが好きPlayer.Items.Where(x => x.IsWeapon)これらが関数内の関数である場合、 y 、 z続きます)。
合計
私は、そのような始まりから客観的な真実を見つけることは容易ではないことを認めます。 例えば、誰かが言う: Serviceを書くか書かないかは論争点であり、重要ではなく、良い味であり、それが機能する場合の違いは何ですか?
しかし、使い捨てカップから飲むことができます!
コンテンツへのパスはフォームを介して行われると確信しており、思考を考慮しない場合は存在しないようです。 プログラムテキストでは、すべてが同じように機能します。スタイル、雰囲気、およびリズムは、混乱せずに、明確かつ容量的に自分自身を表現するのに役立ちます。
オブジェクトの名前は、その顔だけでなく、自分自身でもあります。 それは、それがエーテルであるか飽和しているか、抽象的であるか本物であるか、ドライであるかアニメーションであるかを決定する。 名前は変化しています-コンテンツは変化しています。
次の記事では 、このまさにその内容とそれが起こることについて話します。