Przygotowane w poprzedniej części testy uwidoczniły pewne ułomności zaimplementowanych klas – chcąc przetestować funkcjonalność powiadamiania o postępie przetwarzania pliku, konieczne było wykonanie samego importu. To nasuwa wniosek, że powiadamianie o postępie zależy od samego procesu importu. Co więcej, w jednym z testów tej funkcjonalności nie udało się początkowo uzyskać pozytywnego wyniku. To nasuwa kolejny wniosek – powiadamianie o postępie zależy od konkretnego typu importu. To już bardzo daleko idąca zależność. Koniec końców fakt, że trzeba dokonać rzeczywistego importu, zamiast użyć jego atrapy, sygnalizuje, że wybrane obecnie rozwiązanie – wykorzystanie wzorca metody szablonowej – samo w sobie nie jest dobrym rozwiązaniem. Dlaczego? Powiedzmy, że przyszłoby nam zaimplementować kolejny import. Z czym to się wiąże? Trzeba by utworzyć kolejną klasę potomną, która realizowałaby ów import, ale od innych klas potomnych różniła się jedynie … samą interpretacją odczytanej porcji danych. To jednoznacznie wskazuje na krystalizowanie się kolejnej funkcjonalności – interpretacji danych, obecnie odpowiada za to dedykowana klasa potomna wzorca metody szablonowej. Odpowiada? Hm, czyli mamy jakąś konkretną odpowiedzialność – skoro tak – należy ja zamknąć w klasie.

Przed nami zatem dwa zadania: doprowadzenie do uniezależnienia powiadamiania o postępie od samego procesu importu oraz wbudowanie tego procesu w specjalizowaną klasę.

W tym momencie pozwolę sobie na wskazanie kolejnej zalety testów – problem z ich przygotowaniem wskazuje na niedoskonałość zaimplementowanego rozwiązania – ono powinno dać się dowolnie komponować, być elastyczne, a skoro utrudnia test – takie nie jest. To bardzo ważne, należy zauważyć, że jeszcze przed rozpoczęciem używania klasy w środowisku produkcyjnym zyskujemy informację, że sprawia ona problemy. Jest to informacja nieoceniona – gdyby zdarzyło się to już podczas implementowania funkcjonalności zależnych od klasy, konsekwencją byłoby wstrzymanie tych prac i konieczność poprawy klasy. Sprzężenie zwrotne, jakie dają testy jest więc nieocenione – efektem jego działania będzie dostarczenie rozwiązania, które będzie bliskie doskonałości.

Wracając do meritum. W jaki sposób uniezależnić powiadamianie o postępie od samego procesu importu? Całość tego procesu musi realizować metoda szablonowa, żadna z jego części nie może być implementowana w klasach potomnych. Czy jest to możliwe? Tak – zwiększanie ilości przetworzonych danych musi następować w klasie bazowej. To implikuje, że musi ona posiadać informację o rozmiarze odczytanej porcji danych. Co z kolei implikuje, że klasy potomne muszą raportować rozmiar owej porcji. Zatem znana nam już klasa abstrakcyjna implementująca wzorzec metody szablonowej przyjmie następującą postać:

abstract public class FileOfValuesReader
{
	#region konstruktory
	public FileOfValuesReader(string fileName, Transfer transfer, IProgressNotifier progressNotifier)
	{
		this.fileName = fileName;
		if (progressNotifier == null)
			throw new FileOfValuesReaderException("Nie sprecyzowano klasy powiadamiającej o postępie przetwarzania");
		else
			this.progressNotifier = progressNotifier;
		if (transfer == null)
			throw new FileOfValuesReaderException("Nie sprecyzowano metody przekazującej dane");
		else
			this.transfer = transfer;
	}
	#endregion
	#region właściwości
	abstract protected int Size { get; }
	abstract protected int Readed { get; }
	abstract protected bool EndOf { get; }
	protected string[] Values { get; set; }
	#endregion
	#region metody
	abstract protected void Open();
	abstract protected void Read();
	abstract protected void Extract();
	abstract protected void Close();
	public void Process()
	{
		Open();
		InitializeProgress();
		while (!EndOf)
		{
			Read();
			Extract();
			Transfer();
			RefreshProgress();
		}
	}
	private void Transfer()
	{
		transfer(Values);
	}
	private void InitializeProgress()
	{
		completed = 0;
		progressNotifier.Expected = Size;
		progressNotifier.Completed = completed;
	}
	private void RefreshProgress()
	{
		if (EndOf)
			return;
		completed += Readed;
		progressNotifier.Completed = completed;
	}
	#endregion
	#region pola
	protected string fileName;
	private IProgressNotifier progressNotifier;
	private Transfer transfer;
	private int completed;
	#endregion
}

Tym samym zrealizowaliśmy to co było zamierzone – klasa abstrakcyjna realizuje powiadamianie o postępie przetwarzania, klasy potomne muszą jedynie zwracać w dedykowanej właściwości (Readed) rozmiar odczytanej porcji danych. Wyodrębnienie interpretacji danych do oddzielnej klasy zrealizujemy w kolejnej części cyklu.