Typowe błędy programistów.

Pisałem już kiedyś o błędach popełnianych przez młodych programistów. Byłem wtedy na początku swojej programistycznej kariery i myślałem, że już dużo wiem. Teraz wiem ile jeszcze nie wiem 😉 Niemniej jednak z większością z tamtych punktów zgadzam się do dziś. Postanowiłem więc, po kilku latach jeszcze raz zebrać kilka najczęstszych błędów, które zauważyłem już w aplikacjach enterprise. Poniższe punkty nazwałem błędami architektonicznymi, czyli odnoszącymi się bezpośrednio do kodu programu. Jak wiadomo praca programisty składa się jeszcze z wielu innych aktywności, ale o nich napiszę w innym poście.

1. Nadużywanie statycznych metod

Statyczne metody nie powinny być zbyt często używane. Programiści często idą na skróty i dopisują funkcje statyczne, które zawierają logikę biznesową bądź zmieniają stan aplikacji. Logika biznesowa w metodach statycznych to najgorsze, co może być. Dlaczego? Dlatego, że dla takiej metody jest bardzo trudno napisać jakikolwiek test jednostkowy. Metody statyczne powinny być używane dla bardzo prostych helperów, służących tylko do prostych obliczeń, niewymagających testowania. Chodź i tak do tego lepiej nadają się extension methods. Przykład błędnego użycia metody statycznej znajdziecie poniżej.

var user = User.GetUserById(userId);

public class User
{
    public static User GetUserById(int userId)
    {
        //Implementation
    }
}

Zamiast tego powinno się używać serwisów, które mogą być wstrzyknięte poprzez DI i z łatwością zmockowane.

2. Niekorzystanie z interfejsów

Punkt ten często wiąże się z poprzednim, gdyż metod statycznych nie można umieścić w interfejsie. Często myślimy, że jeśli aktualnie do dostępu do bazy używamy ADO.NET, to tak będzie już zawsze i tworzymy metody pobierające/zapisujące dane w klasie, do której odwołujemy się bezpośrednio. Niestety takie podejście ma bardzo wiele wad. Przede wszystkim, tak samo jak w przypadku metod statycznych bardzo ciężko jest testować taki kod. Nie można stworzyć żadnego mocka, który symulowałby nam dostęp do bazy danych. Drugim problemem z tym związanym jest brak możliwości zmiany aktualnie używanego rozwiązania. Jeśli już w całym programie używa się bezpośrednich połączeń do metod z ADO.NET, to nie przepiszemy wszystkiego na Entity Framework w jeden tydzień.

To co powinno się robić, to używać interfejsów zawsze. Nawet jeśli sposób implementacji w danym momencie jest tylko jeden.

3. Wrzucanie funkcji do jednego worka

Jeśli myślimy zbyt obiektowo, nadchodzi czas gdy biznes przerasta nasze obiektowe pojęcie. Wtedy właśnie zdajemy sobie sprawę, że nasza klasa User trochę się rozrosła i każda operacja mająca cokolwiek wspólnego z użytkownikiem, nie powinna znajdować się w tej jednej klasie. Często osoby, które nie stosują żadnych wzorców architektonicznych, popadają w pułapkę klas Bogów. Zamiast myśleć o obiektach jako encjach bazodanowych, należy pomyśleć o obiektach w kontekście biznesu. Bardzo przydatna w zrozumieniu tego zagadnienia jest książka Domain Driven Design. Pomocne w rozdzieleniu funkcji mogą być również wzorce projektowe, takie jak CQRS.

4. Wykorzystywanie tych samych DTO w wielu, niepowiązanych miejscach.

Najtrudniejsze w programowaniu jest wymyślanie nazw. Dlatego często idziemy na łatwiznę i nadajemy nic nie znaczące nazwy. Załóżmy, że mamy klasę do edycji użytkownika, więc nazwiemy ją UserDto. Po kilku miesiącach zapominamy, piszemy inną funkcjonalność i dajmy na to, że musimy wyświetlić wszystkich użytkowników z daną rolą. Jakiej klasy użyjemy? Prawdopodobnie UserDto, pomijając, że klasa ta posiada znacznie więcej pól niż potrzeba przy prostym wyświetleniu użytkowników. Jest to kolejny błąd, który ciężko naprawić, bo im więcej odwołań do danej klasy tym ta klasa jest trudniej edytowalna.

5. Pisanie nieczytelnych funkcji

Temat pisania nieczytelnych funkcji, poruszany jest w wielu wątkach i opisywany w niejednej książce, mimo to myślę, że wciąż są problemy z czytelnym pisaniem funkcji. Przede wszystkim problemy występują w instrukcjach warunkowych, które potrafią być całkiem sporym kawałkiem logiki. Istnieje nawet kampania anty-ifowa, której członkowie namawiają do programowania bez użycia instrukcji if oraz switch. Nie jestem za popadaniem ze skrajności w skrajność i wiem, że czasami użycie if jest czytelniejsze niż przerobienie architektury, niemniej jednak należy zwrócić uwagę na to, czy inni programiści zrozumieją, kiedy kod wchodzi do bloku if.

Poniższy przykład pokazuje świetnie ewolucje oprogramowania. Pierwsza linijka jest akceptowalna, bo to tylko jeden warunek, do tego w miarę czytelny i logiczny- ma pieniądze, więc może kupić. Po dodaniu funkcjonalności ról, umieszczamy kolejny warunek i już zaczyna się robić mało czytelnie. W tym momencie powinniśmy opakować cały warunek w jakąś ładnie brzmiącą metodę. Niestety wiele razy widziałem, że ostatni krok jest pomijany i zostaje taki tasiemiec z instrukcji warunkowych, nad którym trzeba spędzić kilka minut, żeby go zrozumieć.

// this is readable
if (user.AccountBalance >= productCost) { }

// we add Roles to program and code is ugly
if (user.AccountBalance >= productCost && user.Roles.Any(r => r.AllowInAppPurchases)) { }

// after refactor it is better
if (user.CanBuyProduct(productCost)) { }

To tylko kilka uwag, które zamierzałem przedstawić, żeby przestrzec innych. Często w pośpiechu sam zapominam o niektórych zasadach, ale mam nadzieję, że dzięki opisaniu ich tutaj, sam zacznę się bardziej pilnować. 🙂

Fixing linq typings to work with es6

I’m sure that everyone knows linq. It is library which is very useful and has version not only in C# but also in JavaScript. Using linq in javascript may be useless few years ago but now it’s common problem that we need do some buisiness logic operations in js. I worked on few projects where i used Linq in javascript but lately i wanted to use it in Aurelia which is using es6 (ecmascript 6). First i had a problem how to use npm library in Aurelia which use jspm. I asked about that in stackoverflow and get the answer. But then i started using typescript and everything f**ed up.

First i needed typings for linq, so i googled linq.d.ts and get first answer. I added this to my project and created first typescript class. What happened? Visual studio shows error that cannot find module ‚linq’.

I thought that this is something with npm package. But when i wrote the same import in javascript it worked fine. So i checked linq.d.ts file and tried to changed it to meet the import statement. I didn’t changed any interfaces to be sure that i will have correct typings, but what i changed was module declaration. Original module declaration was:

declare module linq

I changed that to:

declare module 'linq'

Error about module disappear but i got another error.

So i had to move Enumerable variable into module declaration and make this variable default export.

declare module 'linq' {
    
    //Interfaces...

    var Enumerable: EnumerableStatic;
    export default Enumerable;
}

That helped, there is no error anymore. It should work for most of you, but because of my clumsiness i didn’t noticed that linq in some version changed their functions names. In some older version it was PascalCase (with first letter is capital ex. SingleOrDefault()) and now it is the same as all javascript functions so camelCase (ex. singleOrDefault()). So again i had to change my typings. I found the newest one at linqjs codeplex page. Did the same changes (with module name and default export) again and now i can use linq in typescript with ecmascript 6 features.

If anyone interested here you can download linq.d.ts file.