Blog
Blog HelionBlog HelionBlog HelionBlog Helion
  • Artykuły
  • Autorzy
  • Recenzje
  • Konkursy

Wzorce projektowe — null object

    Blog.helion.pl Artykuły Wzorce projektowe — null object
    NastępnyPoprzedni

    Wzorce projektowe — null object

    By Jerzy Piechowiak | Artykuły, Programowanie | Brak komentarzy | 27 października, 2016 | 1

    „Object reference not set to an instance of an object” — taki, pojawiający się w najmniej oczekiwanym momencie komunikat to zmora każdego programisty C#. Przyczyna jest zawsze taka sama, a jest nią wyjątek NullReferenceException. Wspomniany wyjątek i komunikat występują w sytuacji, gdy próbujemy wykonać jakąś operację na obiekcie, który jest nullem. Innymi słowy, oczekiwaliśmy, że jakiś fragment naszej aplikacji zwróci nam gotowy obiekt, ale tego nie zrobił.

     

    Standardowym rozwiązaniem, które przychodzi do głowy większości programistów, jest instrukcja IF na zwróconym wyniku. Jeśli jest to null, po prostu nie wykonamy określonej operacji. W przeciwnym wypadku pójdziemy standardową ścieżką. Czy rzeczywiście to najlepsza opcja?

     

    Problem

    We wstępie przedstawiłem proste rozwiązanie, które przetwarza określony kod warunkowo. Zasadniczo ma ono kilka istotnych problemów, które zostały wyliczone poniżej:

    – jest brzydkie i nienaturalne, zwłaszcza jeśli takich miejsc w kodzie jest więcej;

    – utrudnia przetwarzanie i komplikuje kod — każda dodatkowa instrukcja IF to nowy scenariusz testowy;

    – problem nulla może ciągnąć się przez całą aplikację — zwłaszcza gdy kod go generujący znajduje się bardzo głęboko;

    – łamie zasady SOLID, a konkretniej SRP — klasa, która powinna operować na tym obiekcie, musi się w tej chwili troszczyć o stan klasy zależnej, a nie operować na wynikowym obiekcie.

     

    Null object pattern eliminuje większość tych problemów — i to w naprawdę prosty sposób.

     

    Przykład praktyczny 

    W przykładzie praktycznym wykorzystam kod generatora raportów, który powstał przy okazji wpisów na temat mnemonika SOLID (zobacz wpisy). Poniżej całość dotychczasowego kodu:

    public interface IFileWriter
    {
    	void Open();
    	void WriteData(IEnumerable<string> data);
    	void Close();
    }
    
    public class CsvFileWriter : IFileWriter
    {
    	public CsvFileWriter(string filePath)
    	{
    		Console.WriteLine($"CSV file with path: {filePath}");
    	}
    
    	public void Open()
    	{
    		Console.WriteLine("CSV file open");
    	}
    
    	public void WriteData(IEnumerable<string> data)
    	{
    		Console.WriteLine($"Write {data.Count()} records to CSV file");
    	}
    
    	public void Close()
    	{
    		Console.WriteLine("CSV file close");
    	}
    }
    
    
    
    public class PdfFileWriter : IFileWriter
    {
    	public PdfFileWriter(string filePath)
    	{
    		Console.WriteLine($"Pdf file with path: {filePath}");
    	}
    
    	public void Open()
    	{
    		Console.WriteLine("PDF file open");
    	}
    
    	public void WriteData(IEnumerable<string> data)
    	{
    		Console.WriteLine($"Write {data.Count()} records to PDF file");
    	}
    
    	public void Close()
    	{
    		Console.WriteLine("PDF file close");
    	}
    }
    
    public interface IDatabaseManager
    {
    	IEnumerable<string> GetReportData(DateTime date);
    }
    
    public class MyDatabaseManager : IDatabaseManager
    {
    	public IEnumerable<string> GetReportData(DateTime date)
    	{
    		string str = date.ToString();
    		for(int i = 0; i < 10; ++i)
    		{
    			yield return str + "_" + i;
    		}
    	}
    }
    
    public class ReportGenerator
    {
     public void GenerateReport(IFileWriter fileWriter, DateTime date)
     {
    		IDatabaseManager databaseManager = new MyDatabaseManager();
    		var reportData = databaseManager.GetReportData(date);
    
    		fileWriter.Open();
    		fileWriter.WriteData(reportData);
    		fileWriter.Close();
    	}
    }
    
    public class Program
    {
    	public static void Main(string[] args)
    	{
    		IFileWriter csvFileWriter = new CsvFileWriter("myreport.csv");
    
    		ReportGenerator reportGenerator = new ReportGenerator();
    
    		DateTime dt = new DateTime(2016, 05, 9);
    		reportGenerator.GenerateReport(csvFileWriter, dt);
    	}
    }
    

    Nasze działania skupimy w obszarze klasy Program, gdzie dodamy prostą metodę wytwórczą, która w zależności od przekazanego rozszerzenia pliku będzie zwracać odpowiedni generator. Dodamy również NullFileWriter, który będzie symulował domyślne zachowanie w sytuacji, gdy podane rozszerzenie nie będzie obsługiwane przez system. Zaczniemy od nowej klasy:

    public class NullFileWriter : IFileWriter
    {
    	public NullFileWriter()
    	{
    	}
    
    	public void Open()
    	{
    	}
    
    	public void WriteData(IEnumerable<string> data)
    	{
    	}
    
    	public void Close()
    	{
    	}
    
    }
    

     

    Jak widać, klasa nie robi nic — po prostu jest. Ponieważ implementuje zadeklarowany interfejs w poprawny sposób, możemy z niej skorzystać zawsze. Zrezygnowałem w tym przypadku nawet z przekazywania ścieżki do pliku w konstruktorze. Skoro klasa nie robi nic, to byłby on tu zbędnym przerostem formy nad treścią. Oczywiście, gdyby w produkcyjnym rozwiązaniu zdecydować się na skorzystanie z klasy Activator do tworzenia obiektów, to zastosowanie tego parametru mogłoby się okazać niezbędne.

    Poniżej modyfikacja klasy Program:

    public class Program
    {
    	public static void Main(string[] args)
    	{
    		IFileWriter fileWriter = GetFileWriter("txt", "File.txt");
    
    		ReportGenerator reportGenerator = new ReportGenerator();
    
    		DateTime dt = new DateTime(2016, 05, 9);
    		reportGenerator.GenerateReport(fileWriter, dt);
    	}
    
    	private static IFileWriter GetFileWriter(string extension, string filePath)
    	{
    
    		IFileWriter fileWriter;
    		switch(extension)
    		{
    			case "csv":
    				fileWriter = new CsvFileWriter(filePath);
    				break;
    			case "pdf":
    				fileWriter = new PdfFileWriter(filePath);
    				break;
    			default:
    				fileWriter = new NullFileWriter();
    				break;
    		}
    		return fileWriter;
    	}
    }
    

    W klasie Program dodałem metodę wytwórczą, która w zależności od przekazanego rozszerzenia generuje odpowiednią klasę Writer. Konstrukcja default tworzy obiekt klasy NullFileWriter, dzięki czemu mamy pewność, że wspomniana metoda nigdy nie zwróci null. Oczywiście dla porządku moglibyśmy wyrzucić dubel rozszerzenia z parametru filePath i ustawiać go na bazie parametru extension.

     

    Null object w ogólności zwiększa przejrzystość tworzonego kodu, zgodność z zasadą SRP mnemonika SOLID, a także zabezpiecza nas przed wyjątkiem NullReferenceException. Warto również zwrócić uwagę na opisane w Wikipedii  jego powiązania z innymi wzorcami.

     

    Jerzy Piechowiak

    Altcontroldelete.pl

    c#, Jerzy Piechowiak, null object, programowanie, SOLID, wzorce projektowe
    Avatar

    Jerzy Piechowiak

    Więcej postów od Jerzy Piechowiak

    Podobne posty

    • Wzorzec projektowy strategii

      By Jerzy Piechowiak | Brak komentarzy

      Programowanie, wbrew obiegowym opiniom, nie jest aż takie trudne. Wypuszczenie nawet prostych aplikacji mobilnych czy stron nie wymaga dzisiaj dużych nakładów sił. Największym problemem jest napisanie takiego kodu, który będzie łatwo rozszerzalny i będzie miałCzytaj więcej…

    • Wzorzec projektowy — metoda wytwórcza

      By Jerzy Piechowiak | Brak komentarzy

      Czasem mówi się, że wzorce projektowe uzupełniają braki techniczne w językach programowania. Jeśli spojrzymy na kilka popularnych wzorców, to szybko dostrzeżemy schemat. Wiele z nich krąży wokół szeroko rozumianego polimorfizmu.

    • Wzorzec projektowy singleton

      By Jerzy Piechowiak | Brak komentarzy

      Ze wzorcami projektowymi bywa różnie. Niektóre są bardzo przydatne, inne mniej, a jeszcze inne są pożądane tylko w określonych sytuacjach. Ogólnie bardzo łatwo wpaść w pułapkę nadużycia określonych rozwiązań. Taka sytuacja ma często miejsce wCzytaj więcej…

    • Wzorce projektowe – Template method

      By Jerzy Piechowiak | Brak komentarzy

      Kilka razy spotkałem się z opinią, że wzorce projektowe są pewnym uzupełnieniem języków programowania, ponieważ realizują zadania, które powinny być dostępne out of the box. Trudno polemizować z takim stwierdzeniem w przypadku singletona czy factoryCzytaj więcej…

    • Mnemonik SOLID – D jak Dependency Inversion Principle

      By Jerzy Piechowiak | Brak komentarzy

      Przyszła pora na ostatnią literkę z mnemonika SOLID, ale to wcale nie oznacza, że jest ona najmniej ważna. D rozwijane jest jako Dependency Inversion Principle co tłumaczone jest jako Zasada Odwrócenia Zależności.

    NastępnyPoprzedni

    Znajdź post

    Bądźmy w kontakcie

    Książka dnia

    Amazon Web Services. Podstawy korzystania z chmury AWS

    Autor: Mark Wilkins

    Cena: 44.50 zł 89.00 zł
    (Cena e-booka: 44.50 zł 89.00 zł)

    O 44,5zł taniej!

    kup teraz

    Ostatnie wpisy

    • Błyskawiczny kurs pisania skryptów powłoki
    • Przykładowa aplikacja webowa zaimplementowana w ASP .NET Core
    • Wprowadzenie do .NET Core: instalacja, konfiguracja, pierwsza aplikacja w systemie Linux
    • Grupa Helion zaprasza na szkolenia stacjonarne!
    • Hello World! Czym jest programowanie?

    Tagi

    .net agile altcontroldelete asp.net c# czysty kod debugowanie design patterns e-biznes emarketing Google Google Analytics hacking Jerzy Piechowiak kod kodowanie Krzysztof Marzec książka Maciej Dutko magazyn programista Magdalena Daniłoś marketing MVVM onepress organizacja czasu praca prograista programista programowanie prokrastynacja rafał kocisz reklama rozwój software craftsman SOLID startup techniki programowania testowanie video marketing visual studio WPF wzorce projektowe youtube zarządzanie czasem zarządzanie projektami

    Archiwum

    • lipiec 2017
    • czerwiec 2017
    • maj 2017
    • kwiecień 2017
    • marzec 2017
    • luty 2017
    • styczeń 2017
    • grudzień 2016
    • listopad 2016
    • październik 2016
    • wrzesień 2016
    • lipiec 2016
    • czerwiec 2016
    Blog wydawnictwa
    Informatyka w najlepszym wydaniu
    Strona wydawcy:
    www.helion.pl
    Księgarnia Helion.pl
    Nowości
    Bestsellery
    Promocje
    Bądźmy w kontakcie:
    Chcesz zostać autorem?
    Masz pytania do redakcji?
    Napisz do nas »
    • Artykuły
    • Autorzy
    • Recenzje
    • Konkursy
    Blog Helion