1

I need to run a certain function before every Function of my class. (Not once in a constructor). It basically needs to happen within that function but at the beginning.

I also don't want to do this:

class A {
  shouldRunFirstFunction() {}

  functionA() {
    shouldRunFirst();
    doOtherThings();
  }

  functionB() {
    shouldRunFirst();
    doOtherThings();
  }

  functionC() {
    shouldRunFirst();
    doOtherThings();
  }

  functionD() {
    shouldRunFirst();
    doOtherThings();
  }
}

I think in other languages you can extend from class B and then do something their so a certain function gets executed before a function of class A gets executed.

julemand101
  • 28,470
  • 5
  • 52
  • 48
Yuki
  • 255
  • 1
  • 2
  • 9
  • I don't know of a language that does that, except for constructors e.g. in Java that must call the parent constructor either explicitly or implicitly before the body of the constructor, because the parent must be fully instantiated before any other code runs. What you're describing is like Aspect Oriented programming (experimentally in Java with AspectJ, and in Spring AOP) which is roughly some pattern identifying where to make a change, and the change (code to insert). Also can be mimicked with C macros. – John Bayko Mar 03 '23 at 21:55

2 Answers2

0

It sounds like you want two (or more) classes. One strategy is to put your implementation in one, management in another:

class AImpl {
  shouldRunFirstFunction() {}

  functionA() {
    ...
  }

  ...
}
class A {
  AImpl a;
  getInstance() {
    a.shouldRunFirstFunction();
    return a;
  }
}

To use:

a.getInstance().functionA(();

I don't know your exact use case, so it might be something completely different, but separating the control from the implementation might be what you want.

John Bayko
  • 746
  • 4
  • 7
  • Thanks for you suggestion. I know this approach but I don't want to modify the call of the function. And I don't want to use wrappers. – Yuki Mar 03 '23 at 15:23
  • Well another wrapper could hide the `getInstance()` part, but I can't think of another way of doing what you want without wrappers, maybe mocking like Mockito does, or meta programming. – John Bayko Mar 03 '23 at 22:00
0

There is no good way to do that. The proper thing to do is to create a forwarding class that implements the base interface:

class ForwardingA implements A {
  A a;

  ForwardingA(this.a);

  @override
  void functionA() {
    shouldRunFirstFunction();
    doOtherThings();
  }

  ...
}

And that way you can be sure that you override all of the members of A (if you don't, you will get a compile-time error).

You probably could implement some code-generation solution that, given a class A, generates ForwardingA for you. However, that probably would be way more work, especially if you're doing this for a small number of cases.

Another approach would be to make noSuchMethod forward method calls to a delegate semi-automatically:

class A {
  void functionA() {
    print('A');
  }

  void functionB() {
    print('B');
  }
}

class ForwardingA implements A {
  void Function() preamble;
  A a;

  late final _methods = <Symbol, Function>{
    #functionA: a.functionA,
    #functionB: a.functionB,
  };

  ForwardingA(this.a, this.preamble);

  @override
  dynamic noSuchMethod(Invocation invocation) {
    var f = _methods[invocation.memberName];
    if (f != null) {
      preamble();
      return Function.apply(
        f,
        invocation.positionalArguments,
        invocation.namedArguments,
      );
    }
    
    return super.noSuchMethod(invocation);
  }
}

void main() {
  var foo = ForwardingA(A(), () => print('Hello world!'));
  foo.functionA();
  foo.functionB();
}

which prints:

Hello world!
A
Hello world!
B

However, I would not recommend that since:

  • noSuchMethod will incur a runtime performance penalty.
  • It isn't fully automatic. You need to manually build the Symbol-to-Function Map. Worse, there would be no compile-time protection if you accidentally omit listing one of A's members or make a typo for one of its Symbols.
jamesdlin
  • 81,374
  • 13
  • 159
  • 204