2010-10-25

Prostota vs. estetyka

Wzorce projektowe, zalecenia projektowe platformy i wszystkie podobne powstają w jednym celu: mają ułatwić tworzenie i rozumienie kodu pisanego z użyciem danego API. Czy mówimy o operacjach asynchronicznych w Symbianie, czy o tworzeniu obiektu aplikacji jako globalnego - zawsze celem jest zwiększenie przejrzystości i przewidywalności kodu. Są jednak takie sytuacje, gdy trzymanie się wspomnianej przejrzystości zabiera stanowczo za dużo czasu i rozwiązanie najbardziej oczywiste, "podręcznikowe" jest tym najgorszym.

Pierwszy przykład, czyli łączenie kodu natywnego C++ z zarządzanym (C#.NET). Aplikacja korzysta z MFC i COM, więc pierwszym odruchem staje się... eksport DLLki .NET z wykorzystaniem interfejsu COM i importowanie jej w kodzie natywnym. Wszystko pięknie i ładnie, prawda? Pięknie budujemy DLLkę, równie pięknie eksportujemy interfejs do TLB, importujemy w kodzie natywnym, tworzymy obiekt przez COM i używamy go. Zaraz, zaraz... DLLkę trzeba zarejestrować i musi to zrobić instalator. Do tego, jeśli w momencie instalacji wszystkie zależności nie są spełnione, DLLka się nie zarejestruje i wynikną z tego dalsze problemy.

Jakie więc jest niezalecane i najprostsze rozwiązanie w tym wypadku? Wystarczy pliki odwołujące się do zarządzanego kodu kompilować jako C++/CLI (zarządzany C++). Oczywiście, aplikacja wymaga wtedy platformy .NET (której i tak by wymagała z uwagi na DLLkę), za to łączenie kodu polega na dodaniu jednej zależności między projektami i... użyciu jej w kodzie. Klasy .NET są w miarę kompatybilne z MFC, więc odpada problem konwersji. Problem rozwiązany.

Tak przynajmniej się wydawało. Okazało się, że po włączeniu CLI, aplikacja w momencie zamykania rzuca nieprzechwycony wyjątek i wyświetla mało przyjazne okno z informacją o błędzie. Oczywiście, co pierwsze? Szukałem jaka jest tego przyczyna - przeglądałem kod, wszystkie informacje go dotyczące, śledziłem dokładnie przebieg programu. Dwa dni zajęło mi zrozumienie gdzie i co rzuca wyjątkiem - a nadal nie wiedziałem dlaczego i jak to złapać (wyjątek leciał już poza main i poza moim kodem). Rozwiązanie najprostsze i najskuteczniejsze: zaraz po ostatniej linijce wykonywanego mojego kodu (w trakcie zamykania aplikacji) wystarczyło dodać ::TerminateProcess(::GetCurrentProcess(), 0); - ta prosta instrukcja cicho i bezproblemowo kończy działanie aplikacji ;-)

Wniosek z całej sytuacji jest prosty: wzorce projektowe i zalecenia są dobre, dopóki nie dodają dodatkowej pracy. Jeśli dany problem można bez szkody rozwiązać szybciej, ignorując wspomniane wzorce, po prostu należy to zrobić. Każda zasada, każde zalecenie w programowaniu ma być dla programisty pomocą, a nie utrudnieniem - więc zasada KISS powinna zawsze stać ponad wszelkimi dokumentami i formalizacją.

Brak komentarzy:

Prześlij komentarz