Good design for regular expressions, capture groups, and unit testing

In the project, I experiment using regular expressions to distinguish between different types of sentences and compare them with functions to process these sentences.

Most of these sentence processing functions accept arguments from the sentence itself and are parsed by capture groups in a regular expression.

Example: "I paid $ 20 for 2 cookies," corresponds to one of the regular expressions in my parse tree (dictionary). The regular expression will correspond to the allocation of $ 20 as the group "price", and 2 - as the "sum" of the group. I am currently mapping the correct Handler function and calling it as follows:

foreach(KeyValuePair<Regex, Type> pair in sentenceTypes) { Match match = pair.Key.Match(text); if(match.Success) { IHandler handler = handlerFactory.CreateHandler(pair.Value); output = handler.Handle(match); } } 

An example of a simple handler class.

 public class NoteCookiePriceHandler { public string Handle(Match match) { double payment = Convert.ToDouble(match.Result("${payment}")); int amount = Convert.ToInt32(match.Result("${amount}")); double price = payment / amount; return "The price is $" + price; } } 

I tried to set up some unit tests with Moq to help when I realized that I couldn't really make fun of a Match or Regex object. Thinking about this, the design looks a bit erroneous in general, since I depending on how the named groups are correctly parsed and passed to the Handler class without a good interface.

I am looking for suggestions for a more efficient design for correctly passing the parameters of the passed function / class to the handlers, since passing the Match object seems problematic.

Otherwise, any help in determining how to rig the Regex or Match will be appreciated and at least help me solve my short-term problem. Both of them have no default constructors, and therefore it is difficult for me to get Moq to create objects from them.

Editing: I decided to solve at least a mockery by passing a dictionary of strings for my matching groups, rather than the matching object itself (matching the monom). I am not very happy with this decision, so the recommendations will still be appreciated.

 foreach(KeyValuePair<Regex, Type> pair in sentenceTypes) { match = pair.Key.Match(text); if(match.Success) { IHandler handler= handlerFactory.CreateHandler(pair.Value); foreach (string groupName in pair.Key.GetGroupNames()) { matchGroups.Add(groupName, match.Groups[groupName].Value); } interpretation = handler.Handle(matchGroups); 
+4
source share
1 answer

One way to avoid bad design is to start with the principles of good design, not just the problem you want to solve. This is one of the reasons why test-based development is so strong at transforming code quality. This way of thinking existed before TDD, although under the name: design by contract. Let me demonstrate:

What would you like the perfect handler to look like? How about this:

 interface IHandler { String handle(); } 

Implementation:

 public class NoteCookiePriceHandler : IHandler { private double payment; private int amount; public NoteCookiePriceHandler(double payment, int amount) { this.payment = payment; this.amount = amount; } public String handle() { return "The price is $" + payment / amount; } } 

Now, starting with this perfect design, perhaps with tests for this design. How can we get a quotation proposal proposal that should be sent to processors? Well, the whole problem in computer science can be solved using another layer of indirection. Let's say the offer parser does not create a handler directly, but uses a factory to create it:

 interface HandlerFactory<T> where T: IHandler { T getHandler(KeyValuePair<String, String> captureGroups); } 

Then you can create one factory for each handler, but soon enough you will find a way to create a common factory. Using reflection, for example, you can map the name of the capture group to the constructor parameters. Based on the data types of the constructor parameters, you can automatically let your generic factory handler convert your strings to the correct data types. All this can be easily verified by creating some fake handlers and asking the factory to fill them using some input lines of a pair of value values.

+1
source

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


All Articles