How to reduce grip in this code

At work, I make this application that takes values ​​from an external file (Excel sheet, text file ...) and translates these values ​​into complex instructions that are transferred to another system.

The code below is a bit simplified (no instructions or very simple logic), but the idea remains the same. I have about 60 different translators who manage different types of business logic. Some use only one argument to run. Others accept several arguments.

I have an abstract translator class. The class user will use two public methods: Translate to start the translation logic, and CanTranslate allows you to see if the translator is ready to run.

A developer using this abstract class must implement the DoTranslate method, which will contain the actual business logic. By default, CanTranslate always returns true, but if validation is required, it can be overridden.

Here's the base class of an abstract translator:

// Contains some base logic which is the same for all translators public abstract class BaseTranslator { // Public translate method public void Translate() { if (CanTranslate()) DoTranslate(); } // Checks if we are ready to translate // True by default public virtual bool CanTranslate() { return true; } // This method is used to implement business logic public abstract void DoTranslate(); } 

And here is the implementation of a specific translator class:

 // Translates beer names public class ReverseTranslator : BaseTranslator { // Use of properties to allow strongly typed arguments // which can be seen by the developer at design time public string BeerName { get; set; } // Validation public override bool CanTranslate() { if (BeerName.Equals("Budweiser") || BeerName.Equals("Stella")) return true; else return false; } // Implementation of the business logic public override void DoTranslate() { char[] letters = BeerName.ToCharArray(); Array.Reverse(letters); Console.WriteLine(new string(letters)); } } 

And here is what it looks like when using:

 class Program { public static void Main(string[] args) { var translator = new ReverseTranslator(); translator.BeerName = "Stella"; translator.Translate(); translator.BeerName = "I'm not a beer"; // This line will not translate since it not a valid beer name. translator.Translate(); Console.ReadLine(); } } 

Pro's:

  • Separates specific business logic from small supported units.
  • translators are easy to reuse in other parts of the application.
  • translators can be easily tested
  • allow the translator user to see which arguments are required

My problem:

  • different controller classes are used by many translators. I have too many connections.

I was thinking about using the Factory pattern to create a translator, but then I cannot use properties as argument hints during development.

So, I'm basically looking for a solution in which during development you can easily see which arguments are required. And at the same time, I hope to reduce communication by not having every controller that has 30 new xTranslator instructions.

PS: I will limit myself to using .NET 3.5 for this code.

+4
source share
3 answers

To have development time information for specific parameters of a particular class: you need to work with an instance of a specific class.
To reduce grip in controllers: you need to limit yourself to instances of the abstract class.
You cannot be in the same place at the same time.

By redesigning your overall design, you can eliminate grip and remove development time information.

Transfer the creation and initialization of translators from the controller to the factory or IoC container, which accepts data from your external files as an input sequence (massed, if necessary, in a format with which it can work).

Translators require a constructor that takes a set of arguments.
Benefits:

  • Only a particular class itself should know the details of itself or its parameters.
  • factory needs information on which translator to use in which circumstances.

.

 class TranslatorFactory { //translator lookup table private Dictionary<string, Func<List<string>,BaseTranslator>> Translators = new Dictionary<string,Func<List<string>,BaseTranslator>>{ {"Reverse", (args)=>new ReverseTranslator(args)}, {"Explode", (args)=>new ExplodeTranslator(args)} }; public BaseTranslator GetTranslatorForRow(string command, List<string> arguments) { if(Translators.ContainsKey(command) ) { return Translators[command](arguments); } return null; //or default, or throw exception } } abstract class BaseTranslator { ... public BaseTranslator(List<string> args) { } } class ReverseTranslator: BaseTranslator { public string BeerName {get;set;} public ReverseTranslator(List<string> args) { BeerName = args[0]; } } 

You can go further and remove the relationship between your factory and specific classes, using attributes and reflection to dynamically build your lookup table.

 [TranslatorFor("Reverse")] class ReverseTranslator: BaseTranslator { ... } 
0
source
 different controller classes are using many translators. I have too much coupling 

Controller classes should depend only on the abstraction, which is BaseTranslator . This way you will not have much grip; indeed, it will be a code code. Embedding dependencies in your controllers by injecting dependencies (for example, through constructor options).

One option for your code to depend only on the base type is to create the String property of the Text in the base class:

 BaseTranslator translator = new ReverseTranslator(); // inject implementation translator.Text = "Stella"; translator.Translate(); translator.Text = "I'm not a beer"; translator.Translate(); 
+2
source

It seems to me that there is something contradictory:

  • On the one hand, translators can define their own specific classes (and add new required properties that must be important for a particular class to work correctly)
  • On the other hand, you are dependent on new concrete classes defined by others in your code, as you are the one who instantiates the classes and passes them the correct parameters.

If this is correct, I believe that you did not find the right level of abstraction for your model. In other words, your current abstraction does not help you (more or less) as it expands in each particular class. So I would like to take a look at the model first (but this is just an assumption, since I don't know your model :-)).

Someone here will probably be able to help you better if you can provide a list of 7-10 specific concrete class names and the input values ​​that they would expect us to better understand your domain.

0
source

All Articles