Skip to content

Feature request, Implement Multiton design pattern #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mbnadjib opened this issue Dec 8, 2020 · 2 comments
Closed

Feature request, Implement Multiton design pattern #139

mbnadjib opened this issue Dec 8, 2020 · 2 comments

Comments

@mbnadjib
Copy link

mbnadjib commented Dec 8, 2020

GetIt version: 5.0.1

It would be awsome if we can use Multiton pattern with GetIt.

I am actually developing flutter app which involve services, stores and allot of rxdart input / output streams.

i would like to instanciate / reuse / unregister objects on the fly based on an arbitrary tag (for example: 'category-1-products', 'category-2-products').

At first i went for using registerSingleton with an instanceName but this is not possible since i want to register all my objects at application startup using a setup() function and the tags / instanceName are not yet known.

i realy wanted to do it with GetIt so i figured a way to do it registerFactoryParam and using param1 as the tag

working example

class Product {
  static int instanceNumber = 0;
  final String label;

  Product(this.label) {
    instanceNumber++;
    print('New Instance label: ($label) number: (${instanceNumber})');
  }
}

void main() {
  GetIt.I.registerFactoryParam<Product, String, void>((name, _) {
    if (GetIt.I.isRegistered<Product>(instanceName: name)) {
      return sl<Product>(instanceName: name);
    } else {
      Product instance = Product(name);
      GetIt.I.registerSingleton<Product>(instance, instanceName: name);
      return instance;
    }
  });

  GetIt.I<Product>(param1: 'tag-1'); /// New singleton
  GetIt.I<Product>(param1: 'tag-2'); /// New singleton
  GetIt.I<Product>(param1: 'tag-1'); /// Reuse first singleton
}

I moved on and tried to make clean code so i went to extend GetIt implementation but found out that it is not possible since the _GetItImplementation class is not public

i ended up wrapping the whole library and adding / overriding GetIt methods when required by my app

Working example:

import 'package:get_it/get_it.dart';

/// Type definition for multiton registration callback
typedef MultitonFactoryFunc<T> = T Function(String tag);
class Sl {
  void registerMultiton<T>(MultitonFactoryFunc<T> func,
      {DisposingFunc<T> dispose}) {
    GetIt.I.registerFactoryParam<T, String, void>((tag, _) {
      if (GetIt.I.isRegistered<T>(instanceName: 'multiton-$tag')) {
        return GetIt.I<T>(instanceName: 'multiton-$tag');
      } else {
        T instance = func(tag);
        GetIt.I.registerSingleton<T>(
          instance,
          instanceName: 'multiton-$tag',
          dispose: dispose,
        );
        return instance;
      }
    });
  }

  T get<T>({String instanceName, dynamic param1, dynamic param2, String tag}) {
    assert(tag == null || (param1 == null && param2 == null),
    'cannot use params and tags at the same time');
    return GetIt.I<T>(
        instanceName: instanceName,
        param1: tag != null ? tag : param1,
        param2: param2);
  }

  T call<T>({String instanceName, dynamic param1, dynamic param2, String tag}) {
    return get<T>(
        instanceName: instanceName, param1: param1, param2: param2, tag: tag);
  }

  void unregister<T>(
      {Object instance,
        String instanceName,
        String tag,
        void Function(T) disposingFunction}) {
    GetIt.I.unregister<T>(
      instance: instance,
      instanceName: tag != null ? 'multiton-$tag' : instanceName,
      disposingFunction: disposingFunction, /// this is not working, is submitted and issue see #138
    );
  }

  Future<void> allReady(
      {Duration timeout, bool ignorePendingAsyncCreation = false}) {
    return GetIt.I.allReady(
        timeout: timeout,
        ignorePendingAsyncCreation: ignorePendingAsyncCreation);
  }

  void registerSingleton<T>(T instance,
      {String instanceName, bool signalsReady, DisposingFunc<T> dispose}) {
    return GetIt.I.registerSingleton(instance,
        instanceName: instanceName,
        signalsReady: signalsReady,
        dispose: dispose);
  }
}

Sl sl = Sl();

class Products {
  static int instanceNumber = 0;
  final String categoryLabel;

  Products(this.categoryLabel) {
    instanceNumber++;
    print('New Instance label: ($categoryLabel) number: ($instanceNumber)');
  }

  @override
  String toString() {
    return 'Product: $categoryLabel';
  }

  void dispose() {
    print('never called issue #138');
  }
}

void main() {
  /// Register once use everywhere
  sl.registerMultiton<Products>((tag) => Products(tag), dispose: (p) {
    p.dispose();
  });

  /// instanciate / reuse 
  Products cat1 = sl<Products>(tag: 'category-1-products');
  Products cat2 = sl<Products>(tag: 'category-2-products');
  Products reuseCat1 = sl<Products>(tag: 'category-1-products');

  print(cat1);
  print(cat2);
  print(reuseCat1);

  /// unregister on the fly
  sl.unregister<Products>(tag: 'category-1-products');
}
@escamoteur
Copy link
Collaborator

Why to you need to register all your objects at startup? There is actually no need for that.
Could it be that in reality you want a factory that registers it created instances automatically inside GetIt? And that checks first if an instance with that name already exists?

@escamoteur
Copy link
Collaborator

closing this because of no reply

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants