An attempt to realize polymorphism when one type requires an additional property

I have two interfaces:

interface IDynamicControl { string Id { get; set; } string Label { get; set; } string Value { get; set; } } interface IDynamicList : IDynamicControl { IList ListItems { get; set; } } 

I have a ControlResolver class that returns IDynamicControl - for my purposes, I have wrapper classes for ASP.NET, CheckBoxes, and DropDownLists text fields that implement IDynamicControl (DropDown implements IDynamicList). The bottom line is that I can give resolver the name of the control, for example "Textbox", and it will return to me IDynamicControl, which is the changed ASP.NET text field.

This works fine, but the problem is with DropDownList. Maybe I have a brain fart, but the problem I am facing is that when the control is a drop-down list, I need to explicitly cast to IDynamicList (since resolver returns IDynamicControl), so I can add elements to this to display. This is usually not a problem, but the goal of dynamic control is that I can store the type of the field from the outside and read it, so I would need to do something ugly, like:

 string controlType = SomeService.GetControlType(); if (controlType == "dropdown") { var control = (IDynamicList)ControlResolver.ResolveControl(controlType); // set up list items } else { var control = ControlResolver.ResolveControl(controlType); // stuff with normal controls } 

but it seems pretty ugly. I could include the ListItems property in the database and just throw NotImplemented into classes that don't use it, but this violates the ISP and is even nicer than the if statement. In short, I would like my resolver to return one type of control, so I don't need to have different Resolve () methods, but there is additional work in the consumption code that I have to do if and only if the control is a drop-down menu.

If I'm wrong or I forgot something basic, is there a better solution for this, or should I just use an if statement? I can’t use the base class because all my β€œdynamic” classes inherit from the base classes of the ASP.NET user interface.

+4
source share
4 answers

I think the easiest way to refactor the source code to something cleaner is:

  var control = ControlResolver.ResolveControl(controlType); // ... do common logic for all controls var list = control as IDynamicList; if (list != null) { // .. do additional logic for list controls } 

Your code that calls ControlResolver.ResolveControl should not really know any special rules for drop-down controls. All you really need to know is whether permitted control of the IDynamicList interface is IDynamicList .

Of course, you still have to have conditional logic, but at least the conditions are based on well-known interfaces that your code already knows about, and not on the basic implementation details.

Of course, there are other approaches that you can take to go further along the path of abstraction, but this seems like a small change that can be made without affecting any other code.

0
source

One method cannot return different types if it is not universal. In your case, you cannot pass an argument of a universal type, so in any case you need to apply some kind of explicit narthex. Moreover, since you have a different setup logic, you will also have to apply some conditional checks.

One thing you can get rid of is conditional if checks (just imagine how much if's yu is needed in case of 10 types of controls), you can use IDictionary as a function initialization cache to make the code very clean:

 IDictionary<string, Func<IDynamicControl>> setupWorkerProvider 

and then enable the corresponding configuration function in one line:

 // once setup setup provider setupWorkerProvider.Add("dropdown", (dynamicControl) => { /*setup logic here*/ }); setupWorkerProvider.Add("button", (dynamicControl) => { /*setup logic here*/ }); // resolve initializer var setupWorker = setupWorkerProvider[controlType]; // initialize control setupWorker(control); 
+2
source

It is as it should be. Your ResolveControl method has provided a contractual guarantee that it will return an IDynamicControl. This does not guarantee that he will return something more specific than that.

Imagine if you have the following code:

 public Object foo(int x) { if (x%2 == 0) { return new String("Hello"); } else { return new Integer(1); } } 

Do you expect callers to have all String methods and all available Integer methods?

0
source

I would suggest that since you know you want an IDynamicList, you have a resolver method (ListControlResolver?) That returns it. Perhaps he simply calls your existing resolver and throws it before returning, but you, as the caller, do not care about it. Hell, this can be useful for a later path when you want to control other list-y controls.

0
source

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


All Articles