Is it possible to have a method which derived classes cannot call, but consumers of a class can call?
c#, template-method-pattern
Solution
You should make `TemplateMethod`'s settings immutable:
abstract class TemplateMethod
{
protected TemplateMethod(bool recursive)
{
Recursive = recursive;
}
public bool Recursive { get; private set; }
protected abstract bool ItemIsWhitelisted(SomeType item);
public IEnumerable<SomeType> GetItems() { /* ... */ }
}
class Implementer : TemplateMethod
{
protected override bool ItemIsWhitelisted(SomeType item)
{
Recursive = false; // Oops; Recursive is read-only
return item.CanFrobinate();
}
}
UPD.
Option #2. If it is hard to pass settings via ctor, you may consider injection of immutable parameter object. Something like this (MEF-styled sample):
public interface ISettings
{
bool Recursive { get; }
}
abstract class TemplateMethod
{
[Import]
public ISettings Settings { get; private set; }
protected abstract bool ItemIsWhitelisted(SomeType item);
public IEnumerable<SomeType> GetItems() { /* ... */ }
}
Of course, this means, that `TemplateMethod` can't change settings too.
Option #3. Explicit interface implementation (if `TemplateMethod` should be able to change settings):
public interface ISettingsWriter
{
bool Recursive { set; }
}
abstract class TemplateMethod : ISettingsWriter
{
public bool Recursive { get; private set; }
bool ISettingsWriter.Recursive
{
set { Recursive = value; }
}
protected abstract bool ItemIsWhitelisted(SomeType item);
}
class Implementer : TemplateMethod
{
protected override bool ItemIsWhitelisted(SomeType item)
{
Recursive = true; // Oops; Recursive is still read-only;
return true;
}
}
And of course, this means, that everyone, who wants to change `TemplateMethod.Recursive`, have to cast `TemplateMethod` to `ISettingsWriter`.
Problem
I'm implementing the template method pattern. There are a couple of settings that clients can change on this template method; but I would like to prevent implementers of the template method from changing these settings, because in such cases they might be able to break invariants of the template method (base) class. I'd like such things to fail to compile if possible. (Otherwise tis time to refactor to strategy :) ) Example: ``` abstract class TemplateMethod { // Clients of implementers of this template method change these settings before // calling GetItems() public SomeType RootItem { get; set; } public bool Recursive { get; set; } protected abstract bool ItemIsWhitelisted(SomeType item); public IEnumerable<SomeType> GetItems() { var stack = new Stack<T>(); stack.Push(RootItem); while (!stack.empty()) { var current = stack.Pop(); if (Recursive) { foreach (var item in current.Children) stack.Push(item); } if (!ItemIsWhitelisted(current)) yield return current; } } } class Implementer : TemplateMethod { protected override bool ItemIsWhitelisted(SomeType item) { Recursive = false; // Oops; TemplateMethod.GetItems didn't expect this // to change inside ItemIsWhitelisted return item.CanFrobinate(); } } ``` One method is to refactor to strategy, producing the following: ``` interface IImplementer { bool ItemIswhitelisted(SomeType item); } sealed class NoLongerATemplateMethod { // Clients of implementers of this template method change these settings before // calling GetItems() public SomeType RootItem { get; set; } public bool Recursive { get; set; } public IImplementer impl { get; set; } // would be private set in real code public IEnumerable<SomeType> GetItems() { var stack = new Stack<T>(); stack.Push(RootItem); while (!stack.empty()) { var current = stack.Pop(); if (Recursive) { foreach (var item in current.Children) stack.Push(item); } if (!impl.ItemIsWhitelisted(current)) yield return current; } } } class Implementer : IImplementer { public bool ItemIsWhitelisted(SomeType item) { Recursive = false; // No longer compiles return item.CanFrobinate(); } } ``` I'm curious if there's a language feature which indicates this constraint without applying refactor to strategy.