Переопределение и доопределение методов

Часто нужно создать базовое (архитектурное) решение, которое затем другие программисты будут расширять. При этом понятно, что дополнительные действия неизвестны в это момент, но нужно обеспечить, удобство вызова базовых действий. Известно, что для этого можно метод класса переопределить в классе-наследнике, и при этом можно вызвать метод базового класса:

```
public class C1
{
public virtual void M1()
{
Console.WriteLine("C1.M1");
}
}
```

если требуется вначале базовые действия, затем дополнительные:

```
public class C2 : C1
{
public override void M1()
{
base.M1();
Console.WriteLine("C2.M1");
}
}
```

или наоборот, вначале дополнительные, затем базовые:

```
public class C2 : C1
{
public override void M1()
{
Console.WriteLine("C2.M1");
base.M1();
}
}
```

**Но что делать когда нужно выполнить базовые действия вначале и в заключение, взяв таким образом дополнительные действия в обрамляющие скобки ?**

## Методы "до" и "после"

Прямым, но как правило, совершенно не правильным является следующий способ:

```
public class C1
{
public void BeforM1()
{
Console.WriteLine("C1.BeforM1");
}
public void AfterM1()
{
Console.WriteLine("C1.AfterM1");
}
}
public class C2 : C1
{
public void M1()
{
base.BeforM1();
Console.WriteLine("C2.M1");
base.AfterM1();
}
}
```

Чем плох этот способ :

- В базовом классе вместо одного общего метода стало два, что в частности может привести к дублированию кода, связанного с локальными переменными;
- Потерян полиформизм, что при глубоком наследовании заставляет вызывать разные методы, а не один не зависимо от типа наследников;
- Программист разрабатывающий метод наследник, должен вызвать базовые методы BeforM1() и AfterM1(), и при этом нет гарантий, что он это не забудет сделать.

Полиформизм можно восстановить, следующим образом:

```
public class C1
{
public void BeforM1()
{
Console.WriteLine("C1.BeforM1");
}
public void AfterM1()
{
Console.WriteLine("C1.AfterM1");
}
public virtual void M1()
{
BeforM1();
AfterM1();
}
}
public class C2 : C1
{
public override void M1()
{
base.BeforM1();
Console.WriteLine("C2.M1");
base.AfterM1();
}
}
```

Но тем самым мы добавили еще одну лишнею сущность - метод, который *склеивает* то, что прежде мы зачем то разделили.

## Использование событий

Ряд существенных проблем можно убрать применяя события:

```
public class C1
{
public event EventHandler E1;
public virtual void M1()
{
Console.WriteLine("C1.BeforM1");
if (E1 != null) { E1(this, new EventArgs()); }
Console.WriteLine("C1.AfterM1");
}
}
public class C2 : C1
{
public C2()
{
E1 +=new EventHandler(M1Add);
}
private void M1Add(object sender, EventArgs e)
{
Console.WriteLine("C2.M1");
}
}
```

Этим способом мы убрали необходимость искусственного разделения базового метода на два. А также предоставили возможность программистам, разрабатывающим метод наследника (*прикладные программисты*), доопределить действия, указав требуемый метод. В большинстве случаев этого достаточно. Но иногда, для основополагающих архитектурный действий, требуется жестко задать место в системе, в котором прикладные программисты должны прописать дополнительные действия.

## Прозрачное разделение методов

Но более элегантным и функционально строгим является следующий прием:

```
public class C1
{
public void M1()
{
Console.WriteLine("C1.BeforM1");
M1Realise();
Console.WriteLine("C1.AfterM1");
}
protected virtual void M1Realise()
{
Console.WriteLine("C1.M1");
}
}
public class C2 : C1
{
protected override void M1Realise()
{
Console.WriteLine("C2.M1");
}
}
```

Здесь прикладные программисты вынужденны писать дополнительные методы в четко обозначенном методе M1Realise(), а те кто будет пользоваться этой иерархией класса по прежнему вызывают только метод M1().