Refactoring duplicate code when the only difference is the type of one variable?

I need to be able to connect to two different versions of the API (1.4 and 1.5), call it Foo API. And my code that connects to the API and processes the results is essentially duplicated - the only difference is the data types returned from the two APIs. How can I reorganize this to remove duplication?

In Foo14Connector.cs (my own class that calls API 1.4)

public class Foo14Connector { public void GetAllCustomers() { var _foo = new Foo14WebReference.FooService(); Foo14WebReference.customerEntity[] customers = _foo.getCustomerList; foreach (Foo14WebReference.customerEntity customer in customers) { GetSingleCustomer(customer); } } public void GetSingleCustomer(Foo14WebReference.customerEntity customer) { var id = customer.foo_id; // etc } } 

And in the almost exact duplicate class Foo15Connector.cs (my own class that calls API 1.5)

 public class Foo15Connector { public void GetAllCustomers() { var _foo = new Foo15WebReference.FooService(); Foo15WebReference.customerEntity[] customers = _foo.getCustomerList; foreach (Foo15WebReference.customerEntity customer in customers) { GetSingleCustomer(customer); } } public void GetSingleCustomer(Foo15WebReference.customerEntity customer) { var id = customer.foo_id; // etc } } 

Please note that I have to have two different connectors, because one method call (out of hundreds) in the API has a new parameter in 1.5.

Both classes Foo14WebReference.customerEntity and Foo15WebReference.customerEntity have the same properties.

+4
source share
3 answers

If the connectors are in different projects, this is easy to solve:

Add a new class file, call it ConnectorCommon and copy all the common code, but with the removal of namespaces. Make this class an incomplete class and rename the class (not the file) to something like Connector.

You will need to add a link to this for each project.

Then remove all the code from your current connector classes, rename the class (not necessarily the file) as well as the incomplete class, and add a using statement that references the namespace.

This should get what you are looking for.

So, when you are done, you will have:

File connector:

 public partial class Connector { public void GetAllCustomers() { var _foo = new FooService(); customerEntity[] customers = _foo.getCustomerList; foreach (customerEntity customer in customers) { GetSingleCustomer(customer); } } public void GetSingleCustomer(customerEntity customer) { var id = customer.foo_id; // etc } } 

Magento15Connector File

 using Foo15WebReference; partial class Connector { } 

Magento14Connector File

 using Foo14WebReference; partial class Connector { } 

Update

This process can be a bit confusing at first.

To clarify, you share the source code in a shared file between two projects.

Actual classes are concrete classes with namespaces in each project. You use a partial keyword so that the common file is combined with the actual project file (i.e. Magneto14) in each project to create a complete class inside this project at compile time.

The hardest part is adding a shared file to both projects.

To do this, select the Add Existing Item... menu in the second project, navigate to the shared file and click the right arrow next to the Add button.

Select Add as link from the drop-down menu. This will add a link to the file in the second project. The source code will be included in both projects, and any changes in the shared file will be automatically available in both projects.

Update 2

I sometimes forget how easily VB performs such tasks, as it is a common programming environment.

To do this work in C #, you need to use another trick: Conditional compilation symbols . This makes the beginning of the generic code a little more verbose than I would like, but it still ensures that you can work with one set of generic code.

To use this trick, add a conditional compilation symbol to each project (make sure it is installed for All Configurations ). For example, in the Magento14 project Magento14 add Ver14 and in the Magento15 add Ver15 .

Then, in the shared file, replace the namespace with a structure similar to the following:

 #if Ver14 using Magneto14; namespace Magento14Project #elif Ver15 using Magneto15; namespace Magento15Project #endif 

This will ensure that the correct namespace and application is included based on the project into which the common code is compiled.

Please note that all regular using statements must be stored in a common file (i.e. enough to compile it).

+5
source

If the FooConnectors are not sealed and you control the creation of new instances, you can get your own connectors and implement interfaces at the same time. In C #, you can implement elements by simply inheriting them from the base class!

 public IFooConnector { void GetAllCustomers(); } public MyFoo14Connector : Foo14Connector, IFooConnector { // No need to put any code in here! } 

and then

 IFooConnector connector = new MyFoo14Connector(); connector.GetAllCustomers(); 
+1
source

You should enter an interface that is common to both implementations. If the projects are written in the same language and are in different projects, you can enter a common project that refers to both projects. Then you make the transition to having dependencies only on your interface, which should allow you to swap places in different implementations behind the scenes somewhere using control inversion (google, dependency injection or service locator or factory pattern).

Difficulties for you may be:

1) Public static methods in implementations cannot be exposed to static effects through an interface 2) It is potentially possible to have code in one implementation class, i.e. Foo14Connector or Foo15Connector, which does not make sense to insert into a common interface

0
source

Source: https://habr.com/ru/post/1389388/


All Articles