2007 |
Section 10, Chapter 3 |
Template Pattern
Concept
This pattern defines the skeleton or template for a group of classes as a parent class, and allows inherited members to modify this template without changing the underlying template class.
Use
We want to centralize the core functional logic that a group of classes have in common, and leave the specialized functionality to be implemented by correspending classes.
Design
- Template - The template is the base or abstract class that acts as the skeleton for all the shared functionality that gets inherited to the class instances.
- Concrete - Which can override the methods of the template class or use these inherited methods without overriding them.
Two rules:
- To limit the layers of inheritance to no deeper than two layers.
- If you find you need a variety of functionality for inherited members, and some inherited classes share this functionality and some do not need to, you might try a plug-and-play pattern like the Visitor pattern.
Illustration
The problem:
class Document
{
public Document(string path){...}
public string FilePath{...}
public void Create(){...}
public void Open(FileMode mode, FileAccess access) {...}
public void Close(){...}
public object Read(){...}
public void Write(object writeable) {...}
}
class Application
{
public Document OpenDocument(string path) {...}
public bool CanOpenDocument(string path) {...}
public Document CreateDocument(string path) {...}
}
abstract class Application
{
public abstract Document OpenDocument(string path);
public virtual bool CanOpenDocument(string path) {...}
public abstract Document CreateDocument(string path);
}
abstract class Document
{
public Document(string path) {...}
public string FilePath {...}
public abstract void Create();
protected abstract void Open(FileMode mode, FileAccess access);
protected abstract void Close();
public abstract object Read();
public abstract void Write(object writeable);
}
class TextDocument: Document {
public TextDocument(string path) : base(path) {}
public override void Create() {
_stream = File.Open(FilePath, FileMode.Create, FileAccess.ReadWrite);
_stream.Close();
}
protected override void Open(FileMode mode, FileAccess access) {
_stream = File.Open(FilePath, mode, access);
}
protected override void Close() {
_stream.Close();
}
public override object Read() {
object readable = null;
Open(FileMode.Open, FileAccess.Read);
using(StreamReader sr = new StreamReader(_stream)) {
String line;
// Read and display lines from the file until the end
// of the file is reached.
while ((line = sr.ReadLine()) != null) {
readable += " - " + line;
Console.WriteLine(line);
}
sr.Close();
}
return readable;
}
public override void Write(object writeable) {
Open(FileMode.Append, FileAccess.Write);
using(StreamWriter sw = new StreamWriter(_stream)) {
sw.WriteLine(writeable);
sw.Close();
}
}
}
class DocumentApplication: Application {
private ArrayList _documents = new ArrayList();
public override Document OpenDocument(string path) {
if (!CanOpenDocument(path)) return null;
Document document = new TextDocument(path);
_documents.Add(document);
return document;
}
public override Document CreateDocument(string path) {
if (CanOpenDocument(path)) return null;
Document document = new TextDocument(path);
document.Create();
_documents.Add(document);
return document;
}
}
DocumentApplication application = new DocumentApplication();
Document doc = application.CreateDocument(path);
doc.Write("This is a Test!");
doc.Write("This is another Test!");
Document docReopened = application.OpenDocument(path);
docReopened.Write("This is the 2nd Test!");
docReopened.Write("This is another 2nd Test!");
docReopened.Read();
Note: The pattern is not worth discussing, it has nothing special but a pretty common way to utilize inheritance. |