0

I have an interface MyInterface and a concrete implementation MyImplementation that implements the interface. Now I need to create multiple "Wrappers" that should also implement MyInterface and will be wrapping the original MyImplementation object. Wrapping in a sense that properties and function methods on the wrapper will serve as a kind of Proxy - i.e. performing some checks on the input data and pass it down to the original implementation of MyImplementation. I need to stack the multiple wrappers into each others, i.e. have an MyValidator1, MyValidator2 etc. that can be - depending on the use case - all added to a single object.

Some code to make things clearer:

interface MyInterface {
    method1(arg1, arg2): any;
}

class MyImplementation implements MyInterface {
    method1(arg1, arg2) {
        /* perform checks and or throw */
    }
}

class MyValidator1 implements MyInterface {
    method1(arg1, arg2) {
        /* ... */
    }
}

class MyValidator2 implements MyInterface {
    method1(arg1, arg2) {
        /* ... */
    }
}

let o1 = new MyImplementation();
// only apply validator1
AddValidator1(o1);

let o2 = new MyImplementation();
// only apply validator2
AddValidator2(o2);

let o3 = new MyImplementation();
// apply both validators
AddValidator1(o3);
AddValidator2(o3);

// intended call hierarchicy when calling o3.method1(arg1, arg2)
// MyValidator1.method1(arg1, arg2)
// MyValidator2.method1(arg1, arg2)
// MyImplemenation.method1(arg1, arg2)
// but the order of the validators is not important!

This is just some code suggestion, I am looking for all kinds of patterns that might differ from the above code to achieve this.

It is important that all instances of MyInterface implemenations are encapsulated - they should behave as if they were MyImplemenation to a consumer. The Validators should possibly add private functions or properties.

Whats the best way to do this? Maybe changing the prototype at runtime?

Dynalon
  • 6,577
  • 10
  • 54
  • 84
  • You add validators per instance of per class? Also, Is there only one instance per validator? – Nitzan Tomer Oct 06 '16 at 10:12
  • it looks like you are looking for something akin to a trait.. maybe a mixing would help? – toskv Oct 06 '16 at 10:24
  • The idea is to be able to mix-and-match the validators. One should be able to add any validators to the implementation, without any validator knowing about the other. – Dynalon Oct 06 '16 at 10:28

2 Answers2

0

I don't know TypeScript, but here how it would be done in regular Javascript (ES6 syntax):

class MyInterface {
  foo() {
    throw new Error('To be implemented');
  }
}

class MyImplementation extends MyInterface {
  foo() {
    return 42;
  }
}

function wrapWithValidator1(impl) {
  class MyValidator1 extends MyInterface {
    constructor() {
      for (const key of Object.keys(impl)) {
        this[key] = impl[key];
      }
    }

    foo() {
      validateStuff();
      return impl.foo();
    }
  }

  return new MyValidator1();
}

function wrapWithValidator2(impl) {
  class MyValidator2 extends MyInterface {
    constructor() {
      for (const key of Object.keys(impl)) {
        this[key] = impl[key];
      }
    }

    foo() {
      validateOtherStuff();
      return impl.foo();
    }
  }

  return new MyValidator2();
}

Your validators are wrappers over the implementation because they "extend" it. They inherit from the implementation prototype (and thus all its properties/methods), but they can override an existing method to provide additional behavior.

Use super.myMethod() when you override a method to call the parent method.

Aurelien Ribon
  • 7,548
  • 3
  • 43
  • 54
  • That doesn't work. If you want validator1 and validator2 in one instance, one has to derive from the other, but the validators shouldn' know about each other, they only know the interface. – Dynalon Oct 06 '16 at 10:23
  • Okay I think I get it. I edited my answer. You need to dynamically create your validator classes, since they need to close the implementation you want at runtime. – Aurelien Ribon Oct 06 '16 at 10:28
  • Therefore, if you want both Val1 and Val2, you just need to call both functions in a row: `wrapWithValidator2(wrapWithValidator1(myImpl))` – Aurelien Ribon Oct 06 '16 at 10:29
  • Yes, but think that `MyInterface` not only has a single method, but several others that shouldn't be validated. You have to proxy every single method then. – Dynalon Oct 06 '16 at 10:38
  • See the new code (in constructors), all properties are inherited from the object given at runtime. A cleaner way would be to use the same loops on prototypes, but I'm no longer used to them since ES6 :/ I let you update the code. – Aurelien Ribon Oct 06 '16 at 10:47
0

How about having a list of validators in the MyImplementation class?
Something like:

abstract class Validator implements MyInterface {
    abstract method1(arg1, arg2): any;
}

class MyValidator1 extends Validator {
    method1(arg1, arg2) {
        /* ... */
    }
}

class MyValidator2 extends Validator {
    method1(arg1, arg2) {
        /* ... */
    }
}

class MyImplementation implements MyInterface {
    private validators: Validator[];

    constructor() {
        this.validators = [];
    }

    addValidator(validator: Validator) {
        this.validators.push(validator);
    }

    method1(arg1, arg2) {
        this.validators.every(validator => { /* do something here */ return false; });
        /* perform checks and or throw */
    }
}

(code in playground)

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • Not feasible, as the `MyImplemenation` class must then know about the vlaidators. It should only know the interface as Validators should be able to be supplied by 3rd parties. – Dynalon Oct 06 '16 at 10:39
  • 1
    Fine, then make the `Validator` an interface instead of a class, but the idea is the same – Nitzan Tomer Oct 06 '16 at 10:41