📜 ⬆️ ⬇️

Kodorebus or strategy pattern on .Net 4.0

Recently, while working on one project, an interesting code was born. We immediately began to test our colleagues sharply, asking them to explain what it is, how it works and what it does. Even experienced developers, this code drives into a stupor (after a couple of minutes of hysterical laughter). So, we meet:

Action<Action> action = (Action action) => { action(); }; 

Before looking under habrakat, try to answer some questions (we will pretend that you did not see title of a post):

Have you answered? Then we dive under the cat behind the background and explanations.


I’ll say right away that this is a completely working code written in C # under .Net Framework 4.0. Of course, we deliberately turned it into a cheerful rebus, in the production code it looks like this:
')
  private Action<Action> doWork = work => { work(); }; 

Speaking in Russian, we create a delegate that takes another delegate as a parameter. Now I will tell how he was born, simplifying the existing code as much as possible, for ease of understanding. We had this code (all names are fictional, and coincidences are random):

  public class MyWorker { private void DoWorkInternal() { Thread.Sleep(1000);//simulate work IsWorkDone = true; } public void DoWork() { DoWorkInternal(); } public bool IsWorkDone { get; private set; } } 

And such a test that checks the result of the work:

  [TestMethod] public void TestDoWork() { MyWorker worker = new MyWorker(); Assert.IsFalse(worker.IsWorkDone); worker.DoWork(); Assert.IsTrue(worker.IsWorkDone); } 

Over time, in combat conditions, it turned out that the DoWorkInternal method DoWorkInternal performed for a long time, and the result of its implementation does not affect the further operation of the application (here you can think about what he could do). Naturally, I wanted to make its execution asynchronous:

  public void DoWork() { Task.Factory.StartNew(DoWorkInternal); } 

And, as you, probably, already guessed, thus we threw a few dozen tests, which continued to believe that the work will be done synchronously. We considered it impractical to change the tests, since they did their job right (they checked the fact of the work and its results). Plus, there was absolutely no guarantee that in a month this work would not require synchrony and everything would have to be returned to its original form. After a little analysis of the situation, it can be understood that here we are dealing with two strategies for doing work - synchronous execution and asynchronous. And a one-way strategy degenerates into a delegate:

  public class MyWorker { private Action<Action> doWork = work => { work(); }; private void DoWorkInternal() { Thread.Sleep(1000);//simulate work IsWorkDone = true; } public void DoWork() { doWork(DoWorkInternal); } public bool IsWorkDone { get; private set; } } 

Those. The strategy for the simultaneous execution of the operation is as follows:

  (Action work) => { work(); } 

Strategy for asynchronous execution:

  (Action work) => { Task.Factory.StartNew(work); } 

Let's add a method to our class that will allow changing the default (synchronous) strategy to another:

  public void SetDoWorkStrategy(Action<Action> doWork) { this.doWork = doWork; } 

Is done. Test as evidence:

  [TestMethod] public void TestDoWork() { MyWorker worker = new MyWorker(); Assert.IsFalse(worker.IsWorkDone); worker.DoWork(); Assert.IsTrue(worker.IsWorkDone); MyWorker worker2 = new MyWorker(); Action<Action> asyncWorkStrategy = (Action work) => { Task.Factory.StartNew(work); }; worker2.SetDoWorkStrategy(asyncWorkStrategy); Assert.IsFalse(worker2.IsWorkDone); worker2.DoWork(); Assert.IsFalse(worker2.IsWorkDone);//work is in progress } 

When using an asynchronous strategy, one should have a good understanding of the features of inter-thread interaction - access to controls, exception handling, etc. Otherwise, side effects will be inevitable. On this finish, I hope someone article may be useful.

PS A discussion of the above codebing may be a good topic for a conversation with a candidate for the .Net developer position.

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


All Articles