Tworzę aplikację, gdzie bardzo mocno przestrzegam podziału na logikę biznesową i interfejs użytkownika. Właściwie przede wszystkim zależy mi na tym, aby aplikacja była możliwe jak najbardziej przenośna, pomijając kwestie technologiczne jak np. ograniczenia platformy .NET (piszę w C#, ale aplikacja będzie ostatecznie w ASP.NET).
Tak, więc mam kilka dll-ek, które wykonują różne operacje funkcjonalne. Np. Rejestracja użytkownika, czyli po prostu zapisanie usera do bazy danych. Niestety podczas rejestracji mogą wystąpić różne "wyjątkowe" sytuacje. Tj:
- User może już istnieć na bazie
- Opcja zakładania usera jest wyłączona
- Błędnie podane hasło usera, np. nieprawidłowe znaki lub też za mało znaków etc.
Chciałbym, aby na poziomie GUI było jak najmniej kodu. Właściwie powinno się tam znajdować jedynie wywoływanie controlerów, tak aby uzyskiwać wyniki swoich działań. Robimy, więc:
ControlUser.Registry("Login", "Pass");
I użytkownik zarejestrowany, a wszelkie instrukcje warunkowe zostały w środku wykonane. Niestety tutaj mam problem, gdyż o ile funkcjonalność w ten sposób zadziała, to jak przekazać użytkownikowi komunikaty o "wyjątkowych" sytuacjach?
Wymyśliłem kilka możliwych rozwiązań:
1.) Każda z tych sytuacji to osobny wyjątek. Np:
if (IsLoginInDataBase(login))
throw new IsLoginInDataBaseException("Podany login istnieje już w bazie danych");
Następnie na poziomie GUI wyłapywałbym takie wyjątki:
try
{
ControlUser.Registry("Login", "Pass");
}
catch(IsLoginInDataBaseException)
{
// Tutaj tylko jakiś prosty showWarning o zaistniałym problemie.
}
catch(Exception)
{
// działanie na wypadek faktycznie nie wiadomego wyjątku - czyli formatka z logiem właściwie
}
Niestety rozwiązanie to powoduje, że czuję wewnętrzny niepokój. Po pierwsze wyjątki nie zostały stworzone po to by przekazywać nimi komunikaty, a po drugie sytuacje przedstawione tutaj wcale nie są "wyjątkowe". Są to zwyczajne sytuacje, które można przewidzieć i się przed nimi zabezpieczyć. Rozwiązanie więc słabe.
2.) Każda z tych sytuacji będzie obsługiwana na poziomie GUI korzystając z publicznych metod. np:
try
{
if (ControlUser.IsLoginInDataBase("Login"))
// Tutaj jakiś prosty ShowWarning o tym, że login istnieje na bazie.
ControlUser.Registry("Login", "Pass");
}
catch(Exception)
{
// działanie na wypadek faktycznie nie wiadomego wyjątku - czyli formatka z logiem właściwie
}
Niestety to rozwiązanie powoduje, że:
- Mamy więcej metod publicznych, co powoduje pisanie większej ilości testów oraz zwiększa ryzyko wystąpienia błędu np. poprzez złe wykorzystanie metody głównej (Registry), gdyż programista zewnętrzny nie będzie wiedział jakie funkcje sprawdzające powinny być wywołane, aby nie doprowadzić do niepotrzebnych błęów.
Rozwiązanie, więc wydaje mi się być słabe.
3.) Użycie osobnej klasy obsługującej komunikaty, czyli po prostu przechowującej stringi / stringa i sterowanie tą klasą np. za pomocą wstrzyiwania zależności.
Rozwiązanie na razie mam operacowane w głowie i nieco z abstrakcyjnym myśleniem, ale wyglądałoby to zapewne mniej-więcej tak:
try
{
ControlUser.Registry("Login", "Pass");
ICommunicat comm = DiControl.Get<IComunicat, ControlUser>(); // powiedzmy - czysta abstrakcja na razie
if (comm.IsError())
// Wyświetlamy błąd.
if (comm.IsWarning())
// Wyświetlamy ostrzezenie.
if (comm.IsInformation())
// Wyświetlamy informację.
}
catch(Exception)
{
// działanie na wypadek faktycznie nie wiadomego wyjątku - czyli formatka z logiem właściwie
}
W uproszczeniu oczywiście tak by to mogło wyglądać. Rozwiązanie wydaje mi się najbardziej sensowne, ale nie poczyniłem jeszcze żadnych kroków do jego implementacji.
Czy widzicie jakieś inne możliwości? Jak przekazywać komunikaty użytkownikowi rozdzielając logikę biznesową od interfejsu, nie tworząc przy tym zbyt wiele kodu w GUI?