Combining dependency injection with service locator - flutter

In the following example, a service locator is used to inject dependencies over classes. Is that in any way better approach than resolving dependencies within the class and keep the initialiser (or class interface) clean?
class MyClass {
let service: Service
init(service: Service) {
self.service = service
}
}
class RootClass {
func something() {
let myClass = MyClass(service: ServiceLocator.shared.resolve())
}
}

This is one of the way to register and inject
// Register a class
locator.registerFactory(() => Service());
class Service {}
class MyClass {
final Service service;
MyClass({this.service});
}
class RootClass {
void something() {
final myClass = MyClass(service: locator.get());
}
}

Related

Best way to shrink service class to smaller pieces

Currently making a large app with Flutter and I m stuck on the architecture of service class. There is a service class for the firestore CRUD operations.This class has many methods and I want split it into small pieces. I use an abstract class to protect methods.I find a way with mixins but don't know it's a good one or not.
https://gist.github.com/pMertDogan/fcd301d768f3980a898cec33a9acaa4f.
//Extend CRUDSERVICE rules aka abstract class => Test
mixin Update{
void updateSomething();
}
mixin Read{
void readSomething();
}
//BASE class for CRUDSERVICE
abstract class Test with Update,Read{
doSomeCreateOP(String x);
}
//
class CrudService extends Test with UpdateService , ReadService{
#override
doSomeCreateOP(String x) {
print('crated ' + x);
}
}
mixin UpdateService{
// #override
void updateSomething() {
print('updated');
}
}
mixin ReadService{
// #override
void readSomething() {
print('read');
}
}
void main() {
CrudService croudService = CrudService();
croudService.doSomeCreateOP(' dartSide');
croudService.updateSomething();
croudService.readSomething();
}
CreateService and UpdateService mixins are just sample.I am thinking like if I need update user information all methods are handed by UserServiceMix mixin if it's Friend then its hanled by FriendServiceMix so I can split them like a domain-based.Each mixin is responsible for specific operations.I can manage then on independent files and summary of them with the help of mixin.
Is it good way to go?
I believe it is a good way to go. It is a quite flexible approach. We use it for API versioning as well.
abstract class Service {
void method1();
void method2();
}
mixin Method1V1 {
void method1() {
print("method1");
}
}
mixin Method2V1 {
void method2() {
print("method2");
}
}
mixin Method2V2 {
void method2() {
print("method2 with changed logic");
}
}
class ServiceV1 extends Service with Method1V1, Method2V1 {
}
class ServiceV2 extends Service with Method1V1, Method2V2 {
}
void main() {
final version = 2;
final Service service = version == 1 ? ServiceV1() : ServiceV2();
service.method2();
}

Dart & flutter : How can I pass an implementation of an abstract class to the constructor of another class? [duplicate]

Lets say that I have an abstract class
abstract class OnClickHandler {
void doA();
void doB();
}
I have a class
class MyClass {
OnClickHandler onClickHandler;
MyClass({
this.onClickHandler
})
void someFunction() {
onClickHandler.doA();
}
}
And I have a class
class Main implements onClickHandler {
// This throws me an error
MyClass _myClass = MyClass(onClickHandler = this); // <- Invalid reference to 'this' expression
#override
void doA() {}
#override
void doB() {}
}
How can I say that use the same implementations that the Main class has? or is there an easier/better way to do this?
Your problem is that this does not yet exists since the object are still being created. The construction of Dart objects is done in two phases which can be difficult to understand.
If you change you program to the following it will work:
abstract class OnClickHandler {
void doA();
void doB();
}
class MyClass {
OnClickHandler onClickHandler;
MyClass({this.onClickHandler});
void someFunction() {
onClickHandler.doA();
}
}
class Main implements OnClickHandler {
MyClass _myClass;
Main() {
_myClass = MyClass(onClickHandler: this);
}
#override
void doA() {}
#override
void doB() {}
}
The reason is that code running inside { } in the constructor are executed after the object itself has been created but before the object has been returned from the constructor.

Dagger can not inject ViewModel with KClass

I am moving my project java to kotlin, but got some confusion about KClass and Class
Here is my BaseActivity
abstract class BaseActivity<DB : ViewDataBinding, VM : BaseViewModel> : DaggerAppCompatActivity() {
private lateinit var mCustomDialog: CustomDialog
private lateinit var mViewDataBinding: DB
private lateinit var mViewModel : VM
#Inject
lateinit var viewModelFactory: ViewModelFactory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set Custom Dialog
mCustomDialog = CustomDialog(this, R.style.LoadingDialogStyle)
// Set ViewModel
mViewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModelClass().java)
// Set DataBinding
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId())
mViewDataBinding.lifecycleOwner = this
mViewDataBinding.setVariable(getBindingVariable(), mViewModel)
mViewDataBinding.executePendingBindings()
// Initialize UI
prepareView(savedInstanceState)
}
#LayoutRes
abstract fun getLayoutId(): Int
protected abstract fun getViewModelClass(): KClass<VM>
abstract fun getBindingVariable(): Int
fun getViewModel(): VM {
return mViewModel
}
fun getViewDataBinding() : DB {
return mViewDataBinding
}
I am using protected abstract fun getViewModelClass(): KClass<VM> function for initializing ViewModel class in the function below
ViewModelProviders.of(this, viewModelFactory).get(getViewModelClass().java)
I use ViewModel in activities by this way
class SplashActivity : BaseActivity<ActivitySplashBinding, SplashViewModel>() {
override fun getViewModelClass(): KClass<SplashViewModel> {
return SplashViewModel::class
}
override fun getLayoutId(): Int {
return R.layout.activity_splash
}
override fun getBindingVariable(): Int {
return BR.vm
}
override fun prepareView(savedInstanceState: Bundle?) {
getViewModel().testLog()
}
}
But when I run the project, I got this error
error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.example.example.MyApp> {
^
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.example.example.utils.ViewModelFactory(viewModels)
com.example.example.utils.ViewModelFactory is injected at
com.example.example.base.BaseActivity.viewModelFactory
com.example.example.ui.splash.SplashActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.example.example.di.AppComponent ? com.example.example.di.ActivityBindingsModule_SplashActivityInjector$app_debug.SplashActivitySubcomponent]
So I made some research and find out it is about KClass in my ViewModelKey
Here is ViewModelKey
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
If I do not change my code to Kotlin and use old Java class like this it works properly
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#MapKey
public #interface ViewModelKey {
Class<? extends ViewModel> value();
}
This is my ViewModelFactory class
#Suppress("UNCHECKED_CAST")
class ViewModelFactory #Inject
constructor(private val viewModels: MutableMap<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModels[modelClass]
?: viewModels.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: throw IllegalArgumentException("unknown model class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
My SplashActivityModule
#Module
abstract class SplashActivityModule {
#Binds
#IntoMap
#ViewModelKey(SplashViewModel::class)
internal abstract fun provideSplashViewModel(splashViewModel: SplashViewModel) : ViewModel
}
So how can I use ViewModelKey properly with Kotlin and what is main cause of this error, any help will be appreciated
Your ViewModelKey be like
#MustBeDocumented
#kotlin.annotation.Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
As mentioned this question problem is related to Kotlin version. Using higher than 1.3.30 version solves the problem.

TypeScript: Reference interface in class when using AMD

I set up everything so far to use TypeScript with requirejs. I tested my scenario with to classes and everything works fine but I fail when I try to use interface definitions.
IComponent.ts:
/// <reference path="Actor.ts" />
interface IComponent {
owner: Actor
init()
update(delta: number)
render()
}
Actor.ts:
/// <reference path="IComponent.ts" />
class Actor {
private components: IComponent[] = []
add(component: IComponent) {
component.owner = this;
this.components.push(component);
}
remove(component: IComponent) {
var index = this.components.indexOf(component);
this.components.splice(index, 1);
}
update(delta: number) {
this.components.forEach(c => c.update(delta));
}
render() {
this.components.forEach(c => c.render());
}
init() {
this.components.forEach(c => c.init());
}
}
export = Actor
I get an error in my IComponent.ts: The symbol 'Actor' could not be found. How can I reference my Actor class from my interface?
As it currently stands you have a logical circular reference, Actor depends on IComponent and IComponent depends on Actor.
Your IComponent interface is little use without your Actor class, so my recommended solution would be to place the IComponent interface within your actor.ts file. This way, the dependency is self-sufficient.
export interface IComponent {
owner: Actor;
init();
update(delta: number);
render();
}
export class Actor {
private components: IComponent[] = []
add(component: IComponent) {
component.owner = this;
this.components.push(component);
}
remove(component: IComponent) {
var index = this.components.indexOf(component);
this.components.splice(index, 1);
}
update(delta: number) {
this.components.forEach(c => c.update(delta));
}
render() {
this.components.forEach(c => c.render());
}
init() {
this.components.forEach(c => c.init());
}
}
Alternatively, you could make IComponent depend on an abstraction of Actor rather than on the concrete class (in your case, perhaps an IActor. Now your Actor depends on IComponent, but that is the end of the chain.
export interface IComponent {
owner: IActor;
init();
update(delta: number);
render();
}
export interface IActor {
add(component: IComponent);
remove(component: IComponent);
update(delta: number);
render();
init();
}
Hacky Fix:
This resolves the error in the IComponent.ts file, but not in a nice way.
import Actor = require('actor');
interface IComponent {
owner: Actor;
init();
update(delta: number);
render();
}

StructureMap InstanceInterceptor not being called

I want to intercept the creation of an instance in SM and I'm trying the following but it's not calling the InstanceInterceptor implementation, does anyone know why?
ForRequestedType<IPublishResources>()
.TheDefault
.Is
.OfConcreteType<PublisherService>()
.InterceptWith(new PublisherServiceInterceptor());
The test code uses the ObjectFactory to create instances, and is shown below:
// Given we have a configure object factory in StructureMap...
ObjectFactory.Configure(x => x.AddRegistry(new StructureMapServiceRegistry()));
// When we request a publisher service...
var publisher = ObjectFactory.GetInstance<IPublishResources>();
Cheers
AWC
I could not reproduce your problem in release 2.5.4. Here is my code.
public interface IPublishResources {}
class PublishResources : IPublishResources {}
public class LoggingInterceptor : InstanceInterceptor
{
//this interceptor is a silly example of one
public object Process(object target, IContext context)
{
Console.WriteLine("Interceptor Called");
return context.GetInstance<PublishResources>();
}
}
public class MyRegistry : Registry
{
public MyRegistry()
{
For<IPublishResources>()
.Use<PublishResources>()
.InterceptWith(new LoggingInterceptor());
}
}
[TestFixture]
public class Structuremap_interception_configuraiton
{
[Test]
public void connecting_implementations()
{
var container = new Container(cfg =>
{
cfg.AddRegistry<MyRegistry>();
});
container.GetInstance<IPublishResources>();
}
}
A question. Do you really need to use an Interceptor here? If you only need to define a factory you can do somethign like this.
public interface IPublishResourcesFactory
{
IPublishResources Create();
}
public class MyRegistry : Registry
{
public MyRegistry()
{
For<IPublishResources>().Use(c =>
{
return c.GetInstance<IPublishResourcesFactory>().Create();
});
//or
For<IPublishResources>().Use(c =>
{
//other object building code.
return new PublishResources();
});
}
}