简体   繁体   中英

Design pattern for Implementations with constructors autowired to different arguments

I have an interface with two implementations and now I have to add more implementations.Each implementation interface has different arguments in the constructor getting auto wired and the beans are getting injected. I need to write a common factory method where if I pass in a client and it returns the specific type of implementation with all the auto wired properties being set. Please advise how to do?

Interface Example{
List<String> getData(String url);
boolean isCorrectData(String var1, String var2);
}

Client1:

public class client1 implements Example{
final XclientConfig config;
final RestTemplate template;

@Autowired
public client1(XClientConfig config, @Qualifier(“template1”)  RestTemplate template){
this.config = config;
this.template = template;
}

Client 2:

public class client2 implements Example{
final YclientConfig config;
final RestTemplate template;

@Autowired
public client2(YClientConfig config, @Qualifier(“template2”)  RestTemplate template){
this.config = config;
this.template = template;
}

Now I am trying to write a factory class where I pass in the client name (client 1 or client 2) , the factory will return a fully constructed instance of Example so there shouldn't be any non-fulfilled dependencies. I need all the dependencies to be injected through the factory method. Can you advise.

General description

You should try using reflection in your Factory . Reflection can be set up by creating a properties file of key value pairs (just a basic text file with a name like properties.config ). This allows you to change the implementation in the text file. Example of this format in the text would be something like:

ClientConfiguration,Client1

where ClientConfiguration is the specific key to access, you guessed it, your Client Configuration. and the value returned is "Client1" as a String. If you want to use Client2 simply change the value.

Using reflection on a properties file

The next step is to use reflection to change this string into the actual Client1 type. You can't cast directly to Client1 instead you want to cast to Client Configuration since that will be the parent type of all your implementations. To do this you need a properties object. Then you need an Input stream object to read your properties.config file and add in the properties from that file into your newly made properties object. Not doing that will mean your properties object would be empty and thus wouldn't help you.

Properties prop = new Properties()
InputStream input = new FileInputstream(//put your properties.config filename here)
prop.load(input)
Clientconfig clientconfig = prop.get(//input the key of the implementation you want here)

//for this example the key would be ClientConfiguration and make sure the String matches the one //on file

Using a Reflected Class-type to create instances of that object with properly formatted data

Now that you can get your class-type you can invoke your constructor and set arguments based on the data. I don't know the specific format of the data but I would recommend a CommaSeperatedValue like format where the file contains some delineation by either Client1 or Client2 "data". Then you should have the first line of this delineation be the fields of the class. Ex:

//inside Foo.txt

config,template
config_a,template_a
config_b,template_b
...

Using the data and the class-type to create instances of any class-type regardless of the number of fields

Formatting the data this way allows you to construct these Client objects without calling their constructors in the Factory class like you would usually do like Client1 client1 = new Client1(arg1,arg2) . Instead you can make a Fieldmap (a map of field names as Strings to Feild objects)

//get your file read and into an array of strings or something similar
Map<String,Field> fieldmap = new Hashmap
Constructor constructor = clientconfig.getConstructor
ClientConfig objectinstance = constructor.inkvoke(//fill in every argument with null here)
//put all the pairs in the fieldmap
for(String fieldname://insert first line of file here){
fieldmap.put(fieldname,Field)
}

//for each field in the object, retrieve the feild value from the feildmap and set it 
//in the object using the matching name
for(Field field:objectinstance){
    field.set(objectinstance,fieldmap.get(field.getName))
}

and now you have a fully constructed instance of your class! If you want to change implementations simply change properties.config and the Factory will do the rest. Most of the code belongs in the Factory when actually creating instances of these classes and it is the same per each construction. The only thing that changes is what class is reflected in, but assuming your data is in the right format (described above) you should be able to construct instances of any type you want. You could even take this a step further and turn this into a generic object factory since that's basically what this is, something that constructs instances of any class. note that I don't make any guarantees on copy pasting this but this is generally how I did it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM