Recently after deriving from an abstract class, I realized I started having a lot of similar classes that only changed one method. I researched ways to avoid this and reached the conclussion that the Decorator pattern was probably the best way to avoid it.
So I must derive from the following class:
public abstract class SlowLoadableComponent<T> : LoadableComponent<T>
where T : SlowLoadableComponent<T>
which in turn derives from:
public abstract class LoadableComponent<T> : ILoadableComponent
where T : LoadableComponent<T>
{
...
protected virtual void HandleLoadError(WebDriverException ex)
{
}
protected abstract void ExecuteLoad();
protected abstract bool EvaluateLoadedStatus();
...
}
So when deriving, we need to define ExecuteLoad()
and EvaluateLoadedStatus()
. Example:
public abstract class ExampleSlowLoadableComponent<T> : SlowLoadableComponent<T>
where T : ExampleSlowLoadableComponent<T>
{
protected IWebDriver driver;
protected ExampleSlowLoadableComponent(IWebDriver driver, TimeSpan timeout, IClock clock)
: base(timeout, clock)
{
this.driver = driver;
}
protected override bool EvaluateLoadedStatus()
{
// evaluate loaded status logic
}
protected override void ExecuteLoad()
{
// execute load logic
}
}
The problem I am having is that in some classes ExecuteLoad()
must be changed, while in others, EvaluateLoadedStatus()
is the one that has to be changed, maybe both must be changed.
However I am at a total loss of how to do this.
For example, lets imagine we need to derive an abstract class from the previous ExampleSlowLoadableComponent
a derived class that needs a IWebElement
type defined in the class, and that ExecuteLoad()
changes to use this IWebElement
:
public abstract class SlowLoadableComponentWithButton<T> : ExampleSlowLoadableComponent<T>
where T : SlowLoadableComponentWithButton<T>
{
protected SlowLoadableComponentWithButton(IWebDriver driver, TimeSpan timeout, IClock clock)
: base(driver, timeout, clock)
{
}
protected abstract IWebElement Button { get; }
protected override bool EvaluateLoadedStatus()
{
// evaluate loaded status logic
}
protected override void ExecuteLoad()
{
Button.Click();
}
}
And now, we need a new abstract class deriving from the previous one, but this time the IWebElement
must be injected in the class constructor:
public abstract class SlowLoadableComponentWithButtonInjected<T> : SlowLoadableComponentWithButton<T>
where T : SlowLoadableComponentWithButtonInjected<T>
{
protected SlowLoadableComponentWithButtonInjected(
IWebDriver driver,
IWebElement button,
TimeSpan timeout,
IClock clock) : base(driver, timeout, clock)
{
Button = button;
}
protected override IWebElement Button { get; }
protected override bool EvaluateLoadedStatus()
{
// evaluate loaded status logic
}
}
As you can see, the changes from one class to another are very small, usually just adding one step from the previous one, however I am not sure how to change this pattern into Decorator pattern
?
Any help is appreciated.