web-dev-qa-db-ja.com

類似するが分岐するロジックを処理するための抽象的なファクトリーよりも簡単なアプローチはありますか?

いくつかの許可コンポーネントを現在実装している中規模のAngularベースのWebアプリケーションがあります。全体として、権限コンポーネントが使用される領域は実質的に同じですが、特定のエンドポイント(およびそれらのエンドポイントを構築するために提供する必要があるパラメーター)は少し異なります。

現在のところ、ロジックには4つの別個のブランチがあります(これらのブランチは時間の経過とともに成長する可能性があります)。 4つのブランチは基本的に次のように機能します。

  • 組織
    • グループ
    • ユーザー
  • 事業
    • グループ
    • ユーザー

この設計を容易にするために、いくつかの基本的なインターフェースとクラスを作成しました。

まず、実際のサービスインターフェイスがあります。

export interface PermissionService {
  getPermissionsList(): Observable<PermissionGroup[]>;

  getAppliedPermissions(objectId: number): Observable<AppliedPermission[]>;

  setObjectPermission(objectId: number, permissionId: number, allow: boolean): Observable<any>;

  removeObjectPermission(objectId: number, permissionId: number): Observable<any>;
}

次に、サービスファクトリインターフェースがあります。

export interface PermissionFactory {
  applies(params: PermissionServiceParams): boolean;

  create(params: PermissionServiceParams): PermissionService;
}

ファクトリーは、すべての要求が共有する基本パラメーターと、サービスのタイプを提供する列挙型を含む別のインターフェースPermissionServiceParamsを使用します。

export interface PermissionServiceParams {
  type: PermissionServiceType;
  organization: string;
}

最後に、必要なサービスを作成するために適切なサービスファクトリを選択する責任がある、私の抽象ファクトリの実際の実装があります。それは次のようになります:

@Injectable({
  providedIn: 'root'
})
export class PermissionServiceFactoryService {
  private _factories: PermissionFactory[] = [];

  constructor(organizationUserPermissionFactoryService: OrganizationUserPermissionFactoryService,
              organizationGroupPermissionFactoryService: OrganizationGroupPermissionFactoryService,
              projectUserPermissionFactoryService: ProjectUserPermissionFactoryService,
              projectGroupPermissionFactoryService: ProjectGroupPermissionFactoryService) {
    this._factories.Push(organizationUserPermissionFactoryService);
    this._factories.Push(organizationGroupPermissionFactoryService);
    this._factories.Push(projectUserPermissionFactoryService);
    this._factories.Push(projectGroupPermissionFactoryService);
  }

  getFactory(params: PermissionServiceParams): PermissionFactory {
    const factories = this._factories.filter(fac => fac.applies(params));

    if (!factories || factories.length === 0) {
      throw new Error('No matching factories found!');
    } else if (factories && factories.length > 1) {
      throw new Error('Ambiguous Invocation! Multiple factories apply to the provided params.');
    }

    return factories[0];
  }
}

上記のコードからすぐに明らかでない場合、アルゴリズムは、私の抽象ファクトリーPermissionFactoryの具体的な実装内のアプリケーション内のすべてのPermissionServiceFactoryServiceのリストを収容することで機能します。 getFactoryが呼び出されるたびに、抽象ファクトリーは定義された各許可ファクトリーを反復処理し、メソッドに渡されたパラメーターに適用されるファクトリーを見つけます。

適切なファクトリが見つかると、開発者は次のようにcreateメソッドを自由に呼び出すことができます。

const params = {organization: data.organization, type: PermissionServiceType.OrganizationUserPermissions};
this._permissionsService = permissionServiceFactoryService.getFactory(params).create(params);

ファクトリーは、適切な静的パラメーターを対応する許可サービスのコンストラクターに渡す責任があります。したがって、ベースPermissionServiceParamsに存在するパラメーター以外の追加のパラメーターが必要な場合、ファクトリーがそれを処理します。

これは最も単純なアプローチですか?それはかなり冗長に見えますが、私が達成しようとしていることには少々やり過ぎかもしれません。

3
JD Davis

要するに:No。

あなたはすでにインターフェースを単純化し、バックエンドを「選択」/「接続」する実装にとらわれない手段を提供しました。

使用するために意図したとおりにパターンを使用しています。以下に1つのヒントがありますが、それは何よりも好みの母です。


ただし、同等の複雑さの代替手段があります。

ファクトリを削除し、PermissionService自体(またはその実装)が適切な戦略(または一連の戦略)を選択して各リクエストを完全に満たすようにすることができます。

各リクエストは、どの戦略/戦略をどの順序で試すかについて独自のメリットを考慮しなければならないため、これによりコンテキスト最適化が犠牲になります。

  • マイナス面としては、いくつかの戦略が適用されて全体の実行が遅くなるか、または代わりに要求がより詳細になるように強制される可能性があります。
  • 利点としては、各モジュールに最初に必要なPermissionServiceの種類を理解させることなく、アプリケーション/組織レベルでユーザー/グループを簡単に混合して一致させることができるため、これにより高度な柔軟性が提供されます。

Nit Pick:PermissionServiceFactoryServicePermissionFactoryにもならない理由はありません...

このようにして、より大きなコンテキストで意味のある特定のサブセット/実装を渡すことができます。

export class PermissionServiceFactoryService extends PermissionFactory {
  private _factories: PermissionFactory[] = [];

  constructor(organizationUserPermissionFactoryService: OrganizationUserPermissionFactoryService,
              organizationGroupPermissionFactoryService: OrganizationGroupPermissionFactoryService,
              projectUserPermissionFactoryService: ProjectUserPermissionFactoryService,
              projectGroupPermissionFactoryService: ProjectGroupPermissionFactoryService) {
    this._factories.Push(organizationUserPermissionFactoryService);
    this._factories.Push(organizationGroupPermissionFactoryService);
    this._factories.Push(projectUserPermissionFactoryService);
    this._factories.Push(projectGroupPermissionFactoryService);
  }

  getFactory(params: PermissionServiceParams): PermissionFactory {
    const factories = this._factories.filter(fac => fac.applies(params));

    if (!factories || factories.length === 0) {
      throw new Error('No matching factories found!');
    } else if (factories && factories.length > 1) {
      throw new Error('Ambiguous Invocation! Multiple factories apply to the provided params.');
    }

    return factories[0];
  }

  applies(params: PermissionServiceParams): boolean {
    const factories = this._factories.filter(fac => fac.applies(params));
    return !!factories && factories.length === 1;
  }

  create(params: PermissionServiceParams): PermissionService {
    return getFactory(PermissionServiceParams).create(PermissionServiceParams);
  }
}
3
Kain0_0