Python decorator-like design pattern for Dart/Flutter?

1,084

Anonymous functions in Dart are rather common (unlike Python, where lambda is very restricted).

You therefore could make a helper function that takes the unique part as a callback.

void trySomething(void Function() body) {
  doSomeInitialWork();

  try {
    body();
  } catch (err) {
    throw err;
  } finally {
    doSomeCleanUpWork();
  }
}

void someMethodA() {
  trySomething(() {
    doSomething();
  });
}

void someMethodB() {
  trySomething(() {
    doSomethingElse();
  });
}

That's basically what test() from package:test (or testWidgets() from Flutter) do.


Update for the case described in the comment: It's not much different if the methods return Futures. For example, if you start with:

Future<List<String>> someMethodA() async {
  return await blah();
}

then you could do:

Future<R> trySomethingAsync<R>(Future<R> Function() body) async {
  doSomeInitialWork();

  try {
    return await body();
  } catch (err) {
    throw err;
  } finally {
    doSomeCleanUpWork();
  }
}

Future<List<String>> someMethodA() {
  return trySomethingAsync(() async {
    return await blah();
  });
}
Share:
1,084
user1087973
Author by

user1087973

Updated on December 27, 2022

Comments

  • user1087973
    user1087973 over 1 year

    I'd like to have common try/catch/finally logic in a decorator-like feature that can "wrap" a function or class method. Consider the scenario:

    Class MyClass {
      void someMethodA() {
        doSomeInitialWork();
        
        try {
          doSomething();
        } catch (err) {
          throw err;
        } finally {
          doSomeCleanUpWork();
        }
      }
    
      void someMethodB() {
        doSomeInitialWork();
        
        try {
          doSomethingElse();
        } catch (err) {
          throw err;
        } finally {
          doSomeCleanUpWork();
        }
      }
    }
    

    So on and so forth. The unique parts of each method are just the try body. If I have a bunch of methods, some which require the same logic, is there a "nice" way to avoid redundant code?

    Ideally it could be syntax like:

    @wrapper
    void someMethodA() {
      doSomething();
    }
    
    @wrapper
    void someMethodB() {
      doSomethingElse();
    }
    
    MyClassInstance.someMethodA(); // call it like this and the wrapper takes care of everything
    

    but I know those are annotations in Dart and not applicable here.

    UPDATE

    Following jamesdlin answer, I am trying to incorporate the anonymous function solution to a futures/async/await scenario:

    Future<dynamic> trySomething(Future<dynamic> Function() callback) async {
      doSomeInitialWork();
    
      try {
        return await callback();
      } catch (err) {
        throw err;
      } finally {
        doSomeCleanUpWork();
      }
    }
    
    class MyClass {
      Future<List<String>> someMethodA() async {
        return await trySomething(() async {
          return await someApiCall();
        });
      }
    }
    

    That seems to work, but it looks kind of messy. I'm not sure if what I'm doing in the async/await example is appropriate.

  • user1087973
    user1087973 about 3 years
    To make things a bit more complicated, what if the methods return futures? So instead of void someMethodA() {} it is Future<List<String>> someMethodA() async {return await blah();}. I'm not sure what the anonymous function looks like now and how to wrap the body.
  • jamesdlin
    jamesdlin about 3 years
    @user1087973 Sorry, I didn't see your comment before. If you haven't figured it out on your own yet, I've updated my answer for your case.