Is there anything in Dart that would work like an `abstract mixin`?

34 Views Asked by At

I am trying to write a mockable client for our backend system.

Our setup looks like this:

MyBackend
┣ Base
┃ ┣ my_client.dart
┃ ┣ int_repo.dart
┃ ┗ string_repo.dart
┣ Default Impl
┃ ┣ my_client_impl.dart
┃ ┣ int_repo_impl.dart
┃ ┗ string_repo_impl.dart
┣ Mock Impl
┃ ┣ etc...

What I am trying to accomplish looks something like this:

abstract class MyClient with IntRepo, StringRepo {
    MyClientConfig get config;
}

class MyClientImpl with IntRepoImpl, StringRepoImpl {
    @override
    final MyClientConfig config = ...;

    // No logic needed to support int or string repos
}

class MyClientMock with IntRepoMock, StringRepoMock {
    @override
    final MyClientConfig config = ...;

    // No logic needed to support int or string repos
}

Which would have end points defined as so:

abstract mixin StringRepo {
    Future<String> getString();

    @override
    Future<String> getString() => ...;
}

mixin StringRepoMock extends StringRepo {
    /// Some end point that depends on `MyClientMock.config`
    @override
    Future<String> getString() => ...;
}

Which would be called like this:

void main() async {
    final client = MyClientImpl(...);
    // final client = MyClientMock(...);

    final value = await client.getString();
}

However, this pseudo-code depends on being able to define the abstract mixins IntRepo and StringRepo.

I got close to the behavior I'm looking for by doing the following:

abstract interface class StringRepo {
    Future<String> getString();
}
    
mixin StringRepoImpl on MyClient implements StringRepo {
    /// Some end point that depends on `MyClientImpl.config`
    @override
    Future<String> getString() => ...;
}

Unfortunately, this doesn't seem to be possible, since mixins can't be abstract, nor can they extend each other.

The issue I have with this work-around is that the burden of the interface is borne on MyClient, meaning that if I add getAnotherString(), the error is thrown in my_client_impl.dart, rather than in string_repo_impl.dart.

In other words, MyClientImpl is required to implement getAnotherString(), but I want to force StringRepoImpl to implement it instead.

Am I trying to reinvent the wheel here or is there some language feature that dart is missing that can do this?

0

There are 0 best solutions below