W dotychczasowym procesie refaktoryzacji udało nam się stworzyć elastyczny mechanizm importu – uzyskaliśmy tym samym podstawową funkcjonalność w zadowalającej postaci. Pora zająć się pozostałymi operacjami, które są niezbędne, aby uzyskać kompletną, pierwotną funkcjonalność refaktoryzowanego kodu. Na implementację oczekują przecież: zdefiniowany na początku interfejs IFileSelector i jego pochodna IFilteredFileSelector. Konieczne jest też zaimplementowanie metody Transfer() – do tej pory była ona implementowana jedynie na potrzeby automatycznych testów, jako sposób na potwierdzenie ich poprawności. Należy także pamiętać o – występującym w pierwotnym kodzie – warunku sprawdzającym czy import przebiegł poprawnie i w przypadku gdy nie miało to miejsca – poinformowaniu o tym fakcie użytkownika.

Na pierwszy ogień pójdą interfejsy IFileSelector i IFilteredFileSelector, przypomnijmy sobie ich definicję:

public interface IFileSelector
{
	void Select();
	bool Selected { get; }
	string FileName { get; }
}

public interface IFilteredFileSelector : IFileSelector
{
	string Filter { get; set; }
	string FilterDescription { get; set; }
}

Z punktu widzenia dotychczasowej funkcjonalności zaimplementować należy jedynie drugi z interfejsów, ponieważ użyty pierwotnie OpenFileDialog umożliwia filtrowanie plików i możliwość ta jest skrupulatnie wykorzystywana. Natomiast – jak już poprzednio wspominałem – pierwszy z interfejsów posłuży jedynie do celów korzystania z wyboru pliku. Oto zatem implementacja interfejsu IFilteredFileSelector:

public class WinFormsFilteredFileSelector : IFilteredFileSelector
{
	#region konstruktory
	public WinFormsFilteredFileSelector(OpenFileDialog openFileDialog, string filter = "", string filterDescription = "")
	{
		this.openFileDialog = openFileDialog;
		Filter = filter;
		FilterDescription = filterDescription;
	}
	#endregion
	#region właściwości
	public bool Selected { get { return (FileName != ""); } }
	public string FileName { get; private set; }
	public string Filter { get; set; }
	public string FilterDescription { get; set; }
	#endregion
	#region metody
	public void Select()
	{
		if (string.IsNullOrEmpty(Filter) || string.IsNullOrEmpty(FilterDescription))
			openFileDialog.Filter = "";
		else
			openFileDialog.Filter = FilterDescription + " (" + Filter + ")|" + Filter;
		bool success = (openFileDialog.ShowDialog() == DialogResult.OK);
		if (success)
			FileName = openFileDialog.FileName;
		else
			FileName = "";
	}
	#endregion
	#region pola
	private OpenFileDialog openFileDialog;
	#endregion
}

Jak widać w konstruktorze podawany jest rzeczywisty komponent WinForms, z którego klasa implementująca będzie korzystać (tzw. delegacja zadań), dodatkowo umożliwiono początkowe ustalenie wartości i opisu filtra. Zbudowanie filtra wg konwencji oczekiwanej przez klasę OpenFileDialog następuje już w metodzie Select(). Stwierdzenie, że plik wybrano (właściwość Selected) następuje poprzez sprawdzenie czy nazwa pliku jest ustalona.

Pora teraz zająć się metodą Transfer(). Przyjąłem, że wydzielę ją do dedykowanej klasy o nazwie DataWarehouse. Klasa ta będzie też zawierać metodę Store(), za pomocą której w pierwotnym kodzie zapisywano zaimportowany i przetransformowany do XML-a plik. Przyjrzyjmy się jak będzie wyglądać tak klasa:

public class DataWarehouse
{
	#region właściwości
	public bool Empty { get { return (data == ""); } }
	#endregion
	#region metody
	public void Transfer(params AnyValue[] items)
	{
		if (items.Length < 2)
			return;
		data += "<r s=\"" + items[0].ToString() + "\" q=\"" + items[1].ToString() + "\"/>"
	}

	public void Clear()
	{
		data = "";
	}

	public void Store()
	{
		// ... sposób składowania danych nie jest istotny dla zagadnienia refaktoryzacji ...
	}
	#endregion
	#region pola
	private string data;
	#endregion
}

Ponieważ w naszych rozważaniach o refaktoryzacji nie jest istotny sposób zachowywania zaimportowanych danych, zatem treść metody Store() nie została zaprezentowana. Należy przyjąć, że jakoś będzie ona owe dane zapisywać. Ale z pewnością musi wykonywać ten kod:

string xml = "<data>" + data + "</data>";

Jak widać dodatkowo zaimplementowałem metodę Clear(), która pozwoli wyczyścić dane uzyskane przez metodę Transfer(), tak aby ponowne użycie klasy – w przypadku gdy ta metoda nie zostanie w ogóle wywołana – nie powodowało zachowania danych z poprzedniego importu (zakładając, że klasa DataWarehouse zostanie utworzona raz, po czym będzie wielokrotnie wykorzystywana).
Oprócz dodatkowej metody, jest też właściwość, pozwalająca stwierdzić czy klasa zawiera jakiekolwiek dane. Będzie to przydatne do sprawdzenia czy w ogóle należy dane zachować (no bo skoro ich nie ma…).

Po zaimplementowaniu powyższych klas osiągnęliśmy praktycznie finisz refaktoryzacji. Pozostało jedynie powrócić do wzorca Obiektu Metody i dostosować jego treść do obecnie obowiązującego kodu. Tym zajmę się w następnej, przedostatniej części.