オブジェクト指向プログラミングの世界では、継承の概念は長い間批判されてきました。
多くの議論があります:
- 子クラスは、必ずしも必要ではない親のすべてのデータと動作を継承します(また、親が洗練されると、子クラスは通常、子が開発された時点では予期されていなかったデータと動作を取得します)。
- 仮想メソッドは生産性が低く、言語で非仮想メソッドを宣言できる場合、相続人をオーバーラップする必要がある場合(newという単語でメソッドをマークできますが、ポリモーフィズムは機能せず、そのようなオブジェクトの使用は予期しない動作を引き起こす可能性があります) 、オブジェクトの使用時にキャストされるタイプに応じて)。
- 多重継承が必要な場合、ほとんどの言語では存在せず、存在する場合(C ++)、面倒と見なされます。
- 継承自体が役に立たないタスクがあります-異なるタイプの要素に対して単一の動作を持つ要素のコンテナ(セット、配列、リスト)が必要で、静的型付けを提供する必要がある場合、ジェネリックがここで役立ちます。
- などなど
継承の代替手段は、インターフェイスと構成の使用です。 (インターフェイスは、クラス階層で継承が積極的に使用されている場合でも、多重継承の代替として長い間使用されてきました。)
宣言された継承の利点は、コードの重複がないことです。 サブジェクト領域には、類似または同じプロパティとメソッドのセットを持つオブジェクトがありますが、この動作を実装するための部分的または完全に異なる動作またはメカニズムがあります。 この場合、コードの重複をなくすために他のメカニズムを使用する必要があります。
そして、構成とインターフェイスの場合、この問題をどのように解決できますか(コードの重複がない)?
このトピックはこの記事の主題です。
いくつかのインターフェイスを宣言し、このインターフェイスを実装する2つ以上のクラスを作成します。 インターフェイスを実装するコードの一部、各クラスは異なり、一部は同じです。
単純化するために、インターフェイスのMethodAメソッドがクラスごとに異なる方法で実装され、MethodBメソッドが同じである特定のケースを考慮してください。
頭に浮かぶコードの重複を排除する最初のオプションは、必要な変数を引数として取る静的メソッドを持つヘルパークラスである可能性が高く、これらのメソッドは異なるクラスのmethodB実装で呼び出され、必要な値がヘルパーメソッドに渡されます。
ヘルパークラスは静的として実装する必要はありません。これをストラテジークラスのインスタンスにすることもできます。この場合、入力データをメソッドではなくストラテジーコンストラクターに渡すことをお勧めします。
現代の言語の手段を使用して、このアプローチを具体例でどのように実装できるかを検討することを提案します。 この記事では、C#言語を使用します。 将来的には、JavaとRubyの例を使って続編を書く予定です。
したがって、プロジェクトで、システム内のユーザーを認証できるクラスのセットを実装する必要があります。 認可メソッドはエンティティのインスタンスを返します。これはAuthCredentialsと呼ばれ、ユーザーに関する認可/認証情報が含まれます。 これらのエンティティには、AuthCredentialsの各インスタンスの有効性を検証できる「bool IsValid()」という形式のメソッドが必要です。
ステップ1
上記の問題を解決するために提案されたアプローチの主なアイデアは、アトミックインターフェイスのセット-AuthCredentialsエンティティを表すためのさまざまなオプションと、アトミックインターフェイスの構成であるインターフェイスを作成することです。 各インターフェイスについて、インターフェイスを操作するために必要な拡張メソッドを作成します。 したがって、各インターフェイスの実装については、これらのインターフェイスの実装を操作できる単一のコードが定義されます。 このアプローチの特徴は、拡張メソッドがインターフェイスで定義されたプロパティとメソッドでのみ機能し、インターフェイスの内部データ実装では機能しないことです。
Visual Studio Community 2015では、4つのプロジェクトで構成されるWindowsコンソールアプリケーションのソリューション(ソリューション)を作成します。
- HelloExtensions-ライブラリ(クラスライブラリ)に取り出されたサンプルコードのメインコードが呼び出されるコンソールアプリケーション。
- HelloExtensions.Auth-この記事で検討した問題の解決策を示すためのインターフェースを含むメインライブラリ。
- HelloExtensions.ProjectA.Auth-HelloExtensions.Authで定義されたインターフェイスの実装を含むライブラリ。
- HelloExtensions.ProjectB.Auth-HelloExtensions.Authで定義されたインターフェイスの代替実装を含むライブラリ。
ステップ2
HelloExtensions.Authプロジェクトで次のインターフェースを定義します。 (以下、提案されたインターフェースはデモンストレーション用です;実際のプロジェクトでは、インターフェースの内容はビジネスロジックによって決定されます。)
ICredentialUserインターフェイス-ユーザーがログインまたはその他の識別子を使用して(匿名認証の可能性なしで)ユーザーセッションを作成せずにシステムにログインできる場合。 認証に成功した場合、データベース内のユーザー識別子(UserId)が返され、そうでない場合はnullが返されます。
インターフェースICredentialUser
using System; namespace HelloExtensions.Auth.Interfaces { public interface ICredentialUser { Guid? UserId { get; } } }
ICredentialTokenインターフェース-ユーザーがシステムに匿名でログインできる場合。 認証に成功した場合、セッション識別子(トークン)が返されます。
インターフェースICredentialToken
namespace HelloExtensions.Auth.Interfaces { public interface ICredentialToken { byte[] Token { get; } } }
ICredentialInfoインターフェイス-ログイン(または他の識別子)によるシステムでの従来のユーザー認証の場合、ユーザーセッションの作成。 インターフェイスは、ICredentialUserインターフェイスとICredentialTokenインターフェイスの組み合わせです。
インターフェースICredentialInfo
namespace HelloExtensions.Auth.Interfaces { public interface ICredentialInfo : ICredentialUser, ICredentialToken { } }
IEncryptionKeyインターフェース-システムでの認証に成功すると暗号化キーが返され、ユーザーはデータをシステムに送信する前に暗号化できます。
インターフェースIEncryptionKey
namespace HelloExtensions.Auth.Interfaces { public interface IEncryptionKey { byte[] EncryptionKey { get; } } }
ICredentialInfoExインターフェイスは、ICredentialInfoインターフェイスとIEncryptionKeyインターフェイスを組み合わせたものです。
インターフェースICredentialInfoEx
namespace HelloExtensions.Auth.Interfaces { public interface ICredentialInfoEx : ICredentialInfo, IEncryptionKey { } }
ステップ2.1
HelloExtensions.Authプロジェクトでヘルパークラスと他のデータ型を定義します。 (以下、補助クラスの宣言とロジックは本質的に実証的であり、ロジックはスタブの形式で作成されます。実際のプロジェクトでは、補助クラスはビジネスロジックによって決定されます。)
TokenValidatorクラス-トークンIDを検証するためのロジックを提供します(たとえば、有効な値、内部整合性、およびシステムに登録されているアクティブトークンのセットの存在を確認する)
クラスTokenValidator
namespace HelloExtensions.Auth { public static class TokenValidator { private static class TokenParams { public const int TokenHeaderSize = 8; public const int MinTokenSize = TokenHeaderSize + 32; public const int MaxTokenSize = TokenHeaderSize + 256; } private static int GetTokenBodySize(byte[] token) { int bodySize = 0; for (int i = 0; i < 2; i++) bodySize |= token[i] << i * 8; return bodySize; } private static bool IsValidTokenInternal(byte[] token) { if (GetTokenBodySize(token) != token.Length - TokenParams.TokenHeaderSize) return false; // TODO: Additional Token Validation, // for ex., searching token in a Session Cache Manager return true; } public static bool IsValidToken(byte[] token) => token != null && token.Length >= TokenParams.MinTokenSize && token.Length <= TokenParams.MaxTokenSize && IsValidTokenInternal(token); } }
クラスIdentifierValidator-識別子を検証するためのロジックを提供します(たとえば、システムデータベース内の値の有効性と識別子の存在を確認します)。
クラスIdentifierValidator
using System; namespace HelloExtensions.Auth { public static class IdentifierValidator { // TODO: check id exists in database private static bool IsIdentidierExists(Guid id) => true; public static bool IsValidIdentifier(Guid id) => id != Guid.Empty && IsIdentidierExists(id); public static bool IsValidIdentifier(Guid? id) => id.HasValue && IsValidIdentifier(id.Value); } }
列挙KeySize-暗号化キーの許容サイズ(ビット単位)のリスト、およびバイト単位のキー長としての内部値の定義。
列挙型KeySize
namespace HelloExtensions.Auth { public enum KeySize : int { KeySize256 = 32, KeySize512 = 64, KeySize1024 = 128 } }
KeySizesクラスは、リスト内の暗号化キーの許容サイズのリストです。
クラスKeySizes
using System.Collections.Generic; using System.Collections.ObjectModel; namespace HelloExtensions.Auth { public static class KeySizes { public static IReadOnlyList<KeySize> Items { get; } static KeySizes() { Items = new ReadOnlyCollection<KeySize>( (KeySize[])typeof(KeySize).GetEnumValues() ); } } }
KeyValidatorクラス-暗号化キー検証ロジックを提供します。
クラスKeyValidator
using System.Linq; namespace HelloExtensions.Auth { public static class KeyValidator { private static bool IsValidKeyInternal(byte[] key) { if (key.All(item => item == byte.MinValue)) return false; if (key.All(item => item == byte.MaxValue)) return false; // TODO: Additional Key Validation, for ex., checking for known testings values return true; } public static bool IsValidKey(byte[] key) => key != null && key.Length > 0 && KeySizes.Items.Contains((KeySize)key.Length) && IsValidKeyInternal(key); } }
ステップ2.2
HelloExtensions.AuthプロジェクトでCredentialsExtensionsクラスを定義します。これは、システムでの認証方法に応じて、異なるAuthCredentials構造を宣言する上記で定義されたインターフェースの拡張メソッドを提供します。
クラスCredentialsExtensions
namespace HelloExtensions.Auth { using Interfaces; public static class CredentialsExtensions { public static bool IsValid(this ICredentialUser user) => IdentifierValidator.IsValidIdentifier(user.UserId); public static bool IsValid(this ICredentialToken token) => TokenValidator.IsValidToken(token.Token); public static bool IsValid(this ICredentialInfo info) => ((ICredentialUser)info).IsValid() && ((ICredentialToken)info).IsValid(); public static bool IsValid(this ICredentialInfoEx info) => ((ICredentialInfo)info).IsValid(); public static bool IsValidEx(this ICredentialInfoEx info) => ((ICredentialInfo)info).IsValid() && KeyValidator.IsValidKey(info.EncryptionKey); } }
ご覧のとおり、変数が実装するインターフェイスに応じて、1つまたは別のIsValidメソッドが選択され、AuthCredentialsの構造がチェックされます。コンパイル段階では、最も「完全な」メソッドが常に選択されます。たとえば、ICredentialInfoインターフェイスを実装する変数では、IsValidメソッドが選択されます(このICredentialInfo情報)、IsValid(このICredentialUserユーザー)またはIsValid(このICredentialTokenトークン)ではありません。
ただし、これまでのところ、すべてがそれほど優れているわけではなく、ニュアンスもあります。
- 変数が元の型または最も「完全な」インターフェイスにキャストされる場合、最も「完全な」メソッドを呼び出すときの選択に関するステートメントはtrueです。 また、ICredentialInfoインターフェイスを実装する型の変数がコード内のICredentialUserインターフェイスに持ち込まれる場合、IsValidが呼び出されるとIsValidメソッド(このICredentialUserユーザー)が呼び出され、AuthCredentials構造の不完全/不正な検証につながります。
- IsValid(このICredentialInfoEx情報)とIsValidEx(このICredentialInfoEx情報)の2つのメソッドが同時に存在することはどれくらい正しいですか? ICredentialInfoExインターフェースでは、不完全/誤ったチェックが可能です。
したがって、拡張メソッドの実装の現在のバージョンでは、インターフェイスの「ポリモーフィズム」はありません(条件付きで呼び出します)。
したがって、さまざまなAuthCredentials構造のインターフェイスと、拡張メソッドを持つCredentialsExtensionsクラスを次のように書き換える必要があるようです。
空のIAuthCredentialsインターフェイスを実装します。このインターフェイスから、アトミックインターフェイスが継承されます(AuthCredentials構造のすべてのバリアントの「ルート」インターフェイス)。
(この場合、構成インターフェースを再定義する必要はありません-IAuthCredentialsを自動的に継承します。また、独立した実装を作成することになっていないアトミックインターフェース(この場合はIEncryptionKey)を再定義する必要もありません。)
インターフェースIAuthCredentials
namespace HelloExtensions.Auth.Interfaces { public interface IAuthCredentials { } }
インターフェースICredentialUser
using System; namespace HelloExtensions.Auth.Interfaces { public interface ICredentialUser : IAuthCredentials { Guid? UserId { get; } } }
インターフェースICredentialToken
namespace HelloExtensions.Auth.Interfaces { public interface ICredentialToken : IAuthCredentials { byte[] Token { get; } } }
CredentialsExtensionsクラスでは、IAuthCredentialsで動作するパブリック拡張メソッドを1つだけ残します。
クラスCredentialsExtensions
using System; namespace HelloExtensions.Auth { using Interfaces; public static class CredentialsExtensions { private static bool IsValid(this ICredentialUser user) => IdentifierValidator.IsValidIdentifier(user.UserId); private static bool IsValid(this ICredentialToken token) => TokenValidator.IsValidToken(token.Token); private static bool IsValid(this ICredentialInfo info) => ((ICredentialUser)info).IsValid() && ((ICredentialToken)info).IsValid(); private static bool IsValid(this ICredentialInfoEx info) => ((ICredentialInfo)info).IsValid() && KeyValidator.IsValidKey(info.EncryptionKey); public static bool IsValid(this IAuthCredentials credentials) { if (credentials == null) { //throw new ArgumentNullException(nameof(credentials)); return false; } { var credentialInfoEx = credentials as ICredentialInfoEx; if (credentialInfoEx != null) return credentialInfoEx.IsValid(); } { var credentialInfo = credentials as ICredentialInfo; if (credentialInfo != null) return credentialInfo.IsValid(); } { var credentialUser = credentials as ICredentialUser; if (credentialUser != null) return credentialUser.IsValid(); } { var credentialToken = credentials as ICredentialToken; if (credentialToken != null) return credentialToken.IsValid(); } //throw new ArgumentException( // FormattableString.Invariant( // $"Specified {nameof(IAuthCredentials)} implementation not supported." // ), // nameof(credentials) //); return false; } } }
ご覧のとおり、IsValidメソッドを呼び出すと、変数が実装するインターフェイスのチェックが、コンパイル段階ではなく実行時に実行されるようになりました。
したがって、IsValidメソッド(このIAuthCredentials資格情報)を実装するときは、AuthCredentials構造のチェック結果の正確性を確保するために、正しい順序(最も「完全な」インターフェースから最も「完全でない」まで)でインターフェースの実装をチェックすることが重要です。
ステップ3
HelloExtensions.ProjectA.AuthおよびHelloExtensions.ProjectB.Authプロジェクトに、HelloExtensions.AuthプロジェクトのAuthCredentialsインターフェイスを実装するロジックと、これらのインターフェイスの実装を操作するツールを入力します。
充填プロジェクトの一般原則:
- HelloExtensions.Authからインターフェイスを継承する必要なインターフェイスを定義し、各プロジェクトに固有の宣言を追加します。
- これらのインターフェイスのスタブ実装を作成します。
- 特定のシステムで認証APIを提供するスタブを備えた補助インフラストラクチャを作成します(インフラストラクチャは、原則-インターフェイス、実装、ファクトリに従って作成されます)。
プロジェクト「A」
インターフェース:
インターフェースIXmlSupport
namespace HelloExtensions.ProjectA.Auth.Interfaces { public interface IXmlSupport { void LoadFromXml(string xml); string SaveToXml(); } }
インターフェイスIUserCredentials
using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectA.Auth.Interfaces { public interface IUserCredentials : ICredentialInfo, IXmlSupport { } }
インターフェースIUserCredentialsEx
using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectA.Auth.Interfaces { public interface IUserCredentialsEx : ICredentialInfoEx, IXmlSupport { } }
インターフェースの実装:
クラスUserCredentials
using System; using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectA.Auth.Entities { using Interfaces; public class UserCredentials : IUserCredentials { public Guid? UserId { get; set; } public byte[] SessionToken { get; set; } byte[] ICredentialToken.Token => this.SessionToken; public virtual void LoadFromXml(string xml) { // TODO: Implement loading from XML throw new NotImplementedException(); } public virtual string SaveToXml() { // TODO: Implement saving to XML throw new NotImplementedException(); } } }
注:エンティティ要素の名前は、インターフェースで定義された名前と異なる場合があります。 この場合、インターフェース要素を明示的に実装し、内部のエンティティの対応する要素への呼び出しをラップする必要があります。
クラスUserCredentialsEx
using System; namespace HelloExtensions.ProjectA.Auth.Entities { using Interfaces; public class UserCredentialsEx : UserCredentials, IUserCredentialsEx { public byte[] EncryptionKey { get; set; } public override void LoadFromXml(string xml) { // TODO: Implement loading from XML throw new NotImplementedException(); } public override string SaveToXml() { // TODO: Implement saving to XML throw new NotImplementedException(); } } }
APIインフラストラクチャ:
インターフェースIAuthProvider
namespace HelloExtensions.ProjectA.Auth { using Interfaces; public interface IAuthProvider { IUserCredentials AuthorizeUser(string login, string password); IUserCredentialsEx AuthorizeUserEx(string login, string password); } }
クラスAuthProvider
namespace HelloExtensions.ProjectA.Auth { using Entities; using Interfaces; internal sealed class AuthProvider : IAuthProvider { // TODO: Implement User Authorization public IUserCredentials AuthorizeUser(string login, string password) => new UserCredentials(); // TODO: Implement User Authorization public IUserCredentialsEx AuthorizeUserEx(string login, string password) => new UserCredentialsEx(); } }
クラスAuthProviderFactory
using System; namespace HelloExtensions.ProjectA.Auth { public static class AuthProviderFactory { private static readonly Lazy<IAuthProvider> defaultInstance; static AuthProviderFactory() { defaultInstance = new Lazy<IAuthProvider>(Create); } public static IAuthProvider Create() => new AuthProvider(); public static IAuthProvider Default => defaultInstance.Value; } }
プロジェクト「B」
インターフェース:
インターフェースIJsonSupport
namespace HelloExtensions.ProjectB.Auth.Interfaces { public interface IJsonSupport { void LoadFromJson(string json); string SaveToJson(); } }
インターフェースISimpleUserCredentials
using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectB.Auth.Interfaces { public interface ISimpleUserCredentials : ICredentialUser, IJsonSupport { } }
インターフェイスIUserCredentials
using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectB.Auth.Interfaces { public interface IUserCredentials : ICredentialInfo, IJsonSupport { } }
インターフェースINonRegistrationSessionCredentials
using HelloExtensions.Auth.Interfaces; namespace HelloExtensions.ProjectB.Auth.Interfaces { public interface INonRegistrationSessionCredentials : ICredentialToken, IJsonSupport { } }
インターフェースの実装:
クラスSimpleUserCredentials
using System; namespace HelloExtensions.ProjectB.Auth.Entities { using Interfaces; public class SimpleUserCredentials : ISimpleUserCredentials { public Guid? UserId { get; set; } public virtual void LoadFromJson(string json) { // TODO: Implement loading from JSON throw new NotImplementedException(); } public virtual string SaveToJson() { // TODO: Implement saving to JSON throw new NotImplementedException(); } } }
クラスUserCredentials
using System; namespace HelloExtensions.ProjectB.Auth.Entities { using Interfaces; public class UserCredentials : SimpleUserCredentials, IUserCredentials { public byte[] Token { get; set; } public override void LoadFromJson(string json) { // TODO: Implement loading from JSON throw new NotImplementedException(); } public override string SaveToJson() { // TODO: Implement saving to JSON throw new NotImplementedException(); } } }
クラスNonRegistrationSessionCredentials
using System; namespace HelloExtensions.ProjectB.Auth { using Interfaces; public class NonRegistrationSessionCredentials : INonRegistrationSessionCredentials { public byte[] Token { get; set; } public virtual void LoadFromJson(string json) { // TODO: Implement loading from JSON throw new NotImplementedException(); } public virtual string SaveToJson() { // TODO: Implement saving to JSON throw new NotImplementedException(); } } }
APIインフラストラクチャ:
インターフェースIAuthProvider
namespace HelloExtensions.ProjectB.Auth { using Interfaces; public interface IAuthProvider { INonRegistrationSessionCredentials AuthorizeSession(); ISimpleUserCredentials AuthorizeSimpleUser(string login, string password); IUserCredentials AuthorizeUser(string login, string password); } }
クラスAuthProvide
using System.Security.Cryptography; namespace HelloExtensions.ProjectB.Auth { using Entities; using Interfaces; internal sealed class AuthProvider : IAuthProvider { private static class TokenParams { public const int TokenHeaderSize = 8; public const int TokenBodySize = 64; public const int TokenSize = TokenHeaderSize + TokenBodySize; } private static void FillTokenHeader(byte[] token) { for (int i = 0; i < 2; i++) { token[i] = unchecked( (byte)((uint)TokenParams.TokenBodySize >> i * 8) ); } // TODO: Put Additional Info into the Token Header } private static void FillTokenBody(byte[] token) { using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(token, TokenParams.TokenHeaderSize, TokenParams.TokenBodySize); } } private static void StoreToken(byte[] token) { // TODO: Implement Token Storing in a Session Cache Manager } private static byte[] CreateToken() { byte[] token = new byte[TokenParams.TokenSize]; FillTokenHeader(token); FillTokenBody(token); return token; } public INonRegistrationSessionCredentials AuthorizeSession() { var credentials = new NonRegistrationSessionCredentials() { Token = CreateToken() }; StoreToken(credentials.Token); return credentials; } // TODO: Implement User Authorization public ISimpleUserCredentials AuthorizeSimpleUser(string login, string password) => new SimpleUserCredentials(); // TODO: Implement User Authorization public IUserCredentials AuthorizeUser(string login, string password) => new UserCredentials(); } }
クラスAuthProviderFactory
using System; namespace HelloExtensions.ProjectB.Auth { public static class AuthProviderFactory { private static readonly Lazy<IAuthProvider> defaultInstance; static AuthProviderFactory() { defaultInstance = new Lazy<IAuthProvider>(Create); } public static IAuthProvider Create() => new AuthProvider(); public static IAuthProvider Default => defaultInstance.Value; } }
ステップ3.1
コンソールアプリケーションに、プロジェクト「A」およびプロジェクト「B」プロジェクトからの認証プロバイダーメソッドの呼び出しを入力します。 各メソッドは、IAuthCredentialsを継承するインターフェイスの変数を返します。 変数ごとに、IsValid検証メソッドを呼び出します。 できた
クラスプログラム
using HelloExtensions.Auth; namespace HelloExtensions { static class Program { static void Main(string[] args) { var authCredentialsA = ProjectA.Auth.AuthProviderFactory.Default .AuthorizeUser("user", "password"); bool authCredentialsAIsValid = authCredentialsA.IsValid(); var authCredentialsAEx = ProjectA.Auth.AuthProviderFactory.Default .AuthorizeUserEx("user", "password"); bool authCredentialsAExIsValid = authCredentialsAEx.IsValid(); var authCredentialsBSimple = ProjectB.Auth.AuthProviderFactory.Default .AuthorizeSimpleUser("user", "password"); bool authCredentialsBSimpleIsValid = authCredentialsBSimple.IsValid(); var authCredentialsB = ProjectB.Auth.AuthProviderFactory.Default .AuthorizeUser("user", "password"); bool authCredentialsBIsValid = authCredentialsB.IsValid(); var sessionCredentials = ProjectB.Auth.AuthProviderFactory.Default .AuthorizeSession(); bool sessionCredentialsIsValid = sessionCredentials.IsValid(); } } }
したがって、同様の機能を実装する(また、互いに異なる機能を持つ)さまざまなエンティティに対して、コピーと貼り付けを行わずに単一のメソッドセットを実装して、これらのエンティティを単一の方法で操作できるようになるときに、目標を達成しました。
拡張メソッドを使用するこの方法は、アプリケーションをゼロから設計する場合と、既存のコードをリファクタリングする場合の両方に適しています。
この例のタスクが従来の継承を介して実装されない理由は、別に注目する価値があります:プロジェクト「A」および「B」のエンティティは、各プロジェクトに固有の機能を実装します-最初の場合、エンティティはXMLから/にシリアル化(de)できます- /からJSONへ。
これはデモンストレーションの違いですが、実際のプロジェクトで見られます(エンティティの違いはさらに大きくなる可能性があります)。
言い換えると、機能面で部分的にのみ重複するエンティティの特定のセットがあり、この交差自体が「ファジー」である場合(UserIdとSessionTokenが使用され、他の場所でEncryptionKeyが使用される場合)、機能の交差領域にあるこれらのエンティティは、拡張メソッドに役立ちます。
この記事では、拡張メソッドを使用するための方法論を提案します。
継続する。
更新:
当初、記事では言及されていませんでした-提案されたアプローチは、異なるアセンブリのプロジェクトに既に機能が類似したエンティティ(クラス)があり、これらのトップレベルアセンブリ(クライアントコード)で類似したエンティティで動作する場合により適しています方法は、それらのプロパティを参照し、いくつかのチェックを実行します(すべての結果をコピーして貼り付けます)。
これらのエンティティを改良およびリファクタリングせずに作業の均一性を実装する最良の方法は何ですか?
この場合、一連のインターフェイスを宣言し(インターフェイスのプロパティとメソッドはクラスの既存のプロパティとメソッドを繰り返します)、クラスがこれらのインターフェイスを実装することを宣言することが適切と思われます。
クラスのインターフェイスの実装のいくつかの要素は、クラスの要素が「異なる」と呼ばれ、クラスのリファクタリングが実用的でない場合、クラスの対応する要素を参照することによって明示的に(明示的に)宣言する必要がある可能性があります。
次に、新しいインターフェイスの拡張メソッドを使用してクラスを実装し、クラスにアクセスするすべての場所で、これらのクラスの一部の作業のコピーと貼り付けを1つの拡張メソッドの呼び出しに置き換えます。
したがって、提案されたアプローチは、(特定のコンテキストで)同様の宣言と機能を持つ特定のクラスのセットを使用して作業の均一性を迅速に「修正」および実装する必要がある場合、レガシーコードの作業に適用できます。
(このようなクラスのセットがプロジェクトでどのように表示されるかという問題は、問題外です。)
プロジェクトAPIとクラス階層をゼロから開発する場合、他のアプローチを採用する必要があります。
2つ以上のクラスのメソッドの意味が同じで、ロジックがわずかに異なる場合、コピーアンドペーストなしでコードを実装する方法
おそらくこれは新しい記事のトピックです。