I've got a factory that relies on a call-stack variable to select which type to build. I'd like that Dagger injections would be provided for the built instance and its descendants, without having to actually build all the possible instances in advance, but just the one that is actually selected by the factory. I came up with the following alternatives:
Using ObjectGraph.get()
class Factory {
FooInterface create(FooTypeEnum type) {
switch(type) {
case TYPE_A:
return objectGraph.get(FooA.class);
break;
case TYPE_B:
return objectGraph.get(FooB.class);
break;
}
}
}
Using Lazy (the factory can itself be injected by Dagger)
class Factory {
#Inject Lazy<FooA> fooA;
#Inject Lazy<FooB> fooB;
FooInterface create(FooTypeEnum type) {
switch(type) {
case TYPE_A:
return fooA.get();
break;
case TYPE_B:
return fooB.get();
break;
}
}
}
Pros, cons, other alternatives?
You should prefer the second one, since it explicitly states its dependencies, rather than asking for the whole ObjectGraph. But you probably meant to inject Provider<T>, not Lazy<T>, in order to match the behavior of ObjectGraph.get.
Related
I'm defining some custom Exceptions in Dart.
I want in my logic to check the type of exception and base my processing on that, so I want to create distinct classes for each, for example like this :
class FailedToLoadCriticalDataException implements Exception { } // app cannot continue
class FailedToLoadNonCriticalDataException implements Exception { } // app can continue
However I also want to pass 2 parameters when I create these types of exceptions, the type of API call, and the API url, and the definition for that would look like this :
class UrlCallFailedException implements Exception {
String _dataTypeName;
String _urlEndpoint;
UrlCallFailedException([this._dataTypeName, this._urlEndpoint]);
#override
String toString() {
return "(${this.runtimeType.toString()}) Failed to fetch $_dataTypeName ($_urlEndpoint)";
}
}
Now what I want to do is (replace the initial definitions I made earlier and re)define my FailedToLoadCriticalDataException and FailedToLoadNonCriticalDataException classes so that they are exactly the code that is in the UrlCallFailedException class.
Is there any way to simply say something like class FailedToLoadCriticalDataException **is** UrlCallFailedException; and not need to duplicate the code that defines UrlCallFailedException ?
class FailedToLoadCriticalDataException implements UrlCallFailedException{ } is wrong because it is "Missing concrete implementations of 'getter UrlCallFailedException._dataTypeName',.."
class FailedToLoadCriticalDataException extends UrlCallFailedException{ } is wrong because when I got to throw FailedToLoadNonCriticalDataException("Foo", url); it's expectation is that there are no params ("Too many positional arguments: 0 expected, but 2 found.").
Is there a way to create multiple classes that behave exactly the same as another type and differ only in their class, without duplicating all the code ?
I've come up with this as a decent compromise :
class FailedToLoadCriticalDataException extends UrlCallFailedException {
FailedToLoadCriticalDataException([dataTypeName, urlEndpoint]) {
super._dataTypeName = dataTypeName;
super._urlEndpoint = urlEndpoint;
}
}
class FailedToLoadNonCriticalDataException extends UrlCallFailedException {
FailedToLoadNonCriticalDataException([dataTypeName, urlEndpoint]) {
super._dataTypeName = dataTypeName;
super._urlEndpoint = urlEndpoint;
}
}
Some, but minimal, code duplication, and I can now call throw FailedToLoadNonCriticalDataException("Foo", url); in my code later.
I have a hierarchy of classes: VehicleDTO is a base abstract class.
CarDTO, TruckDTO, VanDTO extend from it.
I have the same hierarchy on the other side of a mapper:
VehicleBO <- CarBO, TruckBO, VanBO.
I want to have all the mapping logic consolidated in one mapper. Period.
I have defined mappings for common attributes, but here is when it becomes interesting, I get this exception during compilation:
The return type ... is an abstract class or interface.
Provide a non abstract / non interface result type or a factory method.
So, how do I specify a factory method, that based on a value of a particular attribute or a class of the pojo, would create a target object for me? I would appreciate a good code snippet that actually does the trick.
Thanks!
You can use a method annotated with #ObjectFactory receiving a source parameter for what you need.
Let's assume that you have a mapper that looks like:
#Mapper
public interface VehicleMapper {
VehicleDTO map(VehicleBO vehicle);
// more
}
If you add a method looking like:
#ObjectFactory
default VehicleDTO createVehicleDto(VehicleBO vehicle) {
// your creation logic
}
Then MapStruct will use the createVehicleDto to create the VehicleDTO object.
NOTE when mapping hierarchies and when the mapping looks like the one in the answer then MapStruct will only map the properties which are in the VehicleDTO class and not in possible implementations of the class. The reason for that is that MapStruct generates the mapping code during compilation and not during runtime.
For mapping hierarchies like what you explained you can do something like the following:
public interface VehicleMapper {
default VehicleDTO map(VehicleBO vehicle) {
if (vehicle instanceOf CarBO) {
return map((CarBO) vehicle);
} else if (vehicle instanceOf TruckBO) {
return map((TruckBO) vehicle);
} else if (vehicle instanceOf VanBO) {
return map((VanBO) vehicle);
} else {
//TODO decide what you want to do
}
}
#Named("car")
CarDTO map(CarBO car);
#Named("truck")
TruckDTO map(TruckBO truck);
#Named("car")
VanDTO map(VanBO van);
// more
}
There is mapstruct/mapstruct#131 requesting for generating code like my example out of the box
Nowadays, maybe using Visitor pattern could be better choice instead of the instanceOf way, check below:
https://techlab.bol.com/en/blog/mapstruct-object-hierarchies
You need to set the subclassExhaustiveStrategy property in your #Mapper annotation to RUNTIME_EXCEPTION.
See Mapstruct documentation:
...
To allow mappings for abstract classes or interfaces you need to set the subclassExhaustiveStrategy to RUNTIME_EXCEPTION, you can do this at the #MapperConfig, #Mapper or #BeanMapping annotations. If you then pass a GrapeDto an IllegalArgumentException will be thrown because it is unknown how to map a GrapeDto. Adding the missing (#SubclassMapping) for it will fix that.
...
I have Pet, Dog and Cat entity classes. Dog and Cat classes extend Pet.
Also I have PetDTO, DogDTO and CatDTO annotated with #JsonSubtype so Jackson resolves well the class of the dtos.
I want to write a mapper using MapStruct that takes a PetDTO entity (can be a DogDTO or a CatDTO) and returns a Dog or a Cat.
For me in this case, the main goal of using a mapping library is to avoid awful code using instanceof.
Any idea? Thanks!
Not currently possible out-of-the-box - see this ticket in mapstruct's GitHub: #366 Support for abstract class mapping or classes with base class. You can try to push it there or maybe contribute this feature yourself. Looks like a reasonable feature to ask for.
I guess that with the current state of affairs this is your best option:
#Mapper
public interface PetMapper {
default PetDTO toPetDto(Pet pet) {
if (pet instanceof Dog) {
return toDogDTO((Dog) pet);
}
if (pet instanceof Cat) {
return toCatDTO((Cat) pet);
}
throw new IllegalArgumentException("Unknown subtype of Pet");
}
default Pet toPetEntity(PetDTO petDTO) {
if (petDTO instanceof DogDTO) {
return toDogEntity((DogDTO) petDTO);
}
if (petDTO instanceof CatDTO) {
return toCatEntity((CatDTO) petDTO);
}
throw new IllegalArgumentException("Unknown subtype of PetDTO");
}
DogDTO toDogDTO(Dog dog);
Dog toDogEntity(DogDTO dogDTO);
CatDTO toCatDTO(Cat cat);
Cat toCatEntity(CatDTO catDTO);
}
The way I ended up implementing a Mapper for a similar case as above was using a combination of a switch-type, with MapStruct Update Existing and creation Mappers.
In my case a property on the source object dictated the subclass we had to generate.
I initially had different mappers for each subtype, but the duplication of the common mapped properties just seemed wrong. So I came up with the following, leveraging the ability of MapStruct to use updating mappers in order to tackle the common parent type properties:
import org.mapstruct.*;
#Mapper
#Named("QualifierPetMapper")
public interface PetMapper {
#Named("DelegatingPetMapper")
#BeanMapping(ignoreByDefault = true)
default PetTarget mapPet(PetSource petSource) {
switch (petSource.getPetType()) {
case "DOG":
DogTarget dogTarget = mapDog(petSource);
updatePet(dogTarget, petSource);
return (dogTarget);
case "CAT":
CatTarget catTarget = mapCat(petSource);
updatePet(catTarget, petSource);
return (catTarget);
default:
throw new CustomException("Unsupported Pet type: "+ petSource.getPetType());
}
}
#BeanMapping(ignoreByDefault = true)
// Specific mappings for Dog
#Mapping(target = "dogfood.name", source = "dogfoodName")
DogTarget mapDog(PetSource petSource);
#BeanMapping(ignoreByDefault = true)
// Specific mappings for Cat
#Mapping(target = "fish.name", source = "favoriteFish")
CatTarget mapCat(PetSource petSource);
#Named("RootPetMapper")
#BeanMapping(ignoreByDefault = true)
// Common properties for Pet
#Mapping(target = "weight.value", source = "weightValue")
#Mapping(target = "name.value", source = "petName")
#Mapping(target = "color", source = "mainColor")
void updatePet(#MappingTarget PetTarget petTarget, PetSource petSource);
}
Here's my use case:
I have an interface with 4 different implementations WorkflowA, WorkflowB, WorkflowC and WorkflowD.
export interface IWorkflow {
getQuestions();
validateQuestions();
getWorkflowSteps();
}
I have a WorkflowManager class that returns the appropriate workflow based on an input variable (Factory pattern?)
export class WorkflowManager {
workflow:IWorkflow;
getWorkflow(workflow){
switch (workflow) {
case 'workflowA':
return new workflowA();
case 'workflowB':
return new workflowB();
case 'workflowC':
return new workflowC();
case 'workflowD':
return new workflowD();
default:
throw new Error(`Unrecognized workflow: ${workflow}`);
}
}
}
I also have a component WorkflowComponent that gets loaded
Example:
/workflow=WorkflowA loads WorkflowComponent. The component then extracts the routeparam workflow and based on the value, it then passes it to the WorkflowManager which returns the appropriate workflow.
Here's a snippet of my component
#Component({
...
})
export class Workflow implements OnInit {
workflowInstance: IWorkflow;
constructor(
private route: ActivatedRoute,
private workflowManager: WorkflowManager,
) {
}
ngOnInit() {
this.route.params.subscribe(params => {
let regType: string = params['workflow'];
this.workflowInstance = this.workflowManager.getWorkflow(workflow);
});
}
}
But I don't like the idea of manually instantiating Workflow* classes. How can I improve this so I wire it using Angular2 supplied API so that it can be managed/injected in to other components?
Also in my current implementation, I will have to make ajax calls outside of Angular framework which I don't think is the best thing to do.
Any ideas on how to take advantage of the Angular framework and improve this?
One way is to inject an injector and acquire the workflow instance imperatively like:
#Injectable()
export class WorkflowManager {
constructor(private injector:Injector) {}
workflow:IWorkflow;
getWorkflow(workflow){
switch (workflow) {
case 'workflowA':
return this.injector.get(workflowA);
case 'workflowB':
return this.injector.get(workflowB);
case 'workflowC':
return this.injector.get(workflowC);
case 'workflowD':
return this.injector.get(workflowD);
default:
throw new Error(`Unrecognized workflow: ${workflow}`);
}
}
}
Alternatively you can inject workflowA, workflowB, workflowC, workflowA and just return the right one depending on the workflow but I think using the injector is a better fit for this use case.
Ensure you provide workflowA, workflowB, workflowC.
I know that GWT doesn't emulate this method, but I need smth that provide its functionality.
I have tried next approach:
private static <T extends Widget> T createWidget(Class<T> widgetClass) {
return GWT.create(widgetClass);
}
But when I try to compile it I get an error:
Only class literals may be used as arguments to GWT.create()
So, how can I write a foresaid method that will emulate Class#newInstance?
GWT.create() always needs the class literal as argument, which means that you has to pass this: GWT.create(MyClass.class) and no other thing.
This is so because the gwt compiler has to decide which class to pick up in compile time, note that in your code the class is passed in runtime.
If you are planing to use GWT.create for a reduced and well known set of classes you can do something like that:
private static <T extends Widget> T createWidget(Class<T> widgetClass) {
if (ClassA.class.equals(widgetClass)) {
return GWT.create(ClassA.class);
} else if (ClassA.class.equals(widgetClass)) {
return GWT.create(ClassB.class);
}
return null;
}