1

I have created a basic, domain-specific application framework for a web-based GUI. The framework provides Undo/Redo of 'Operations', persistence, and other more domain-specific functionality. I am now attempting to package the core non-domain-specific functionality for reuse in other projects.

I have a class ApplicationFramework that looks like this (Typescript):

export class ApplicationFramework {

  constructor() { }

  private transactionWrapper(fn) {
    // ...
  }

  operation(operation: IOperationConstructor, params: any): IOperation {

    // ...

    const me = this;
    let op;
    this.transactionWrapper(function (data) {
      op = new operation(me, data, params);
      if (!op.apply()) {
        op.cancel();
      }
    });

    // ...

    return op;
  }

}

And I have some interfaces and an abstract class for Operations that look like this:

export type Map = Immutable.Map<any, any>;

export interface IOperation {
  resultMessage: string;
  apply(): boolean;
  cancel(): void;
}

export interface IOperationConstructor {
  new(doc: ApplicationFramework, map: Map, params: any): IOperation;
}

export abstract class Operation implements IOperation {

  protected doc: ApplicationFramework;
  protected map: Map;
  protected params: any;

  abstract resultMessage: string;

  constructor(doc: ApplicationFramework, map: Map, params: any) { }

  abstract apply(): boolean;

  abstract cancel(): void;

}

As a side note, within a single implementation, there should be 1 definition of the ApplicationFramework and 1 or more definitions for Operation. For instance, there could be Operations like 'MoveObject', 'RenameEntity', 'ModifyShape', etc. that each extend Operation.

My intention is to make the entire framework extendable to other domains. In that regard, my current approach is for both the ApplicationFramework and Operation classes to be extended to support domain specific use cases.

I am concerned that I am using an anti-pattern. As you can see, the abstract class Operation depends on ApplicationFramework. This is not correct, given the fact that ApplicationFramework and Operation should both be extended and so derived classes of Operation will require a derived class of ApplicationFramework, not ApplicationFramework itself. I've considered creating an interface IApplicationFramework to use in this case, but this still doesn't 'feel right'.

I have 2 questions:
1) Do you think it is a bad idea to generalize my framework in this manner?
2) If not, can you recommend a particular pattern/approach to this problem?

I appreciate any consideration at all.

--Edit--

I've decided to proceed with this approach. I made the doc member of the Operation class abstract so derived classes must define their own implementation of doc:

export abstract class Operation implements IOperation {

  protected abstract doc: ApplicationFramework;
  protected map: Map;
  protected params: IOperationParameters;

  abstract resultMessage: string;

  constructor(doc: ApplicationFramework, map: Map, params: IOperationParameters) { }

  abstract apply(): boolean;

  abstract cancel(): void;

}

Implementation:

class AFImpl extends ApplicationFramework {
  constructor() {
    super();
  }
  AddEntity(map: Map, entity: any) {
    // do stuff
  }
}

class OpImpl extends Operation {
  resultMessage = 'the result';
  doc: AFImpl;
  constructor(doc: AFImpl, map: Map, params: IOperationParameters) {
    super(doc, map, params);
    this.doc = doc;
  }
  apply(): boolean {
    this.doc.AddEntity(this.map, {});
    return true;
  }
}

I am still curious to hear any thoughts on this if you are inclined to share.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.