czwartek, 19 maja 2011

Aby być dobrym programistą...

Moim zdaniem dobry programista to człowiek mający nieustanny związek z programowaniem.
Nabyta wiedza nie czyni człowieka koderem. Aby nauczyć się analitycznego myślenia i z łatwością
rozwiązywać większość problemów algorytmicznych, należy bardzo często wykorzystywać swoją wiedzę w praktyce - programować. Obojętnie co to będzie własny kompilator czy mała aplikacja w javascript.

Kolejnym ważnym elementem jest pisanie Unit testów. Testy jednostkowe pozwolą wykluczyć niepoprawne działanie skomplikowanego kodu, który na pozór wydawałby się idealny. Należy rozpatrzyć wszystkie możliwości. Przykładowo dla:

If(warunek)
{
instrukcje...
}
else
{
instrukcje;
}
powinniśmy zrobić przynajmniej dwa rodzaje testów - gdy program pójdzie ścieżką if oraz else.

W przypadku zagnieżdżania ifów, liczba Unit testów wzrasta.

Programista powinien poznać (nie koniecznie szczegółowo) więcej typów języków programowania co pozwoli na dobranie odpowiedniego narzędzia do danego problemu. Powinien znać ich różnice.

Dalsza część wkrótce...

Własny parser

Każdy dobry programista powinien chociaż raz w życiu napisać własny analizator składniowy.

//Więcej o moim parserze wkrótce...

Inferencja typów i statyczna typizacja

Inferencja typów jest udogodnieniem dla programisty pozwalającym na nie pisanie typów, a zrzucającym ten obowiązek na kompilator .Udogodnienie to jest dostępne w przypadku języków typowanych statycznie tj. java czy c++. a polega to na określaniu typów wyrażeń podczas kompilacji programu.


[1]Procedura inferencji jest prosta: wylicza się typ ciała funkcji oraz jej argumentu, po czym jeśli:
  • typ funkcja nie jest funkcyjny - oznacza to błąd "not a function"
  • funkcja jest typu funkcyjnego, ale typ jej argumentu nie zgadza się z typem podanego argumentu - oznacza to błąd "argument type mismatch"
  • jeśli funkcja jest typu funkcyjnego i typy argumentów się zgadzają wyrażenie ma wartość taką jaką zwraca funkcja

[1] - źródło encyklopedia internetowa

Typy generyczne

Dzięki typom generycznym istnieje możliwość parametryzacji klasy. Zatem uniknąć można rzutowania, które może niejednemu programiście sprawić mały kłopot.
Zapis typu generycznego wygląda tak:

<dowolna_wielka_litera>.

Pozwala to przykładowo na utworzenie klasy przechowującej elementy jakiegoś typu.

class Przechowaj <T>   // klasa przechowująca różne elementy
{
private T el;            // referencja do obiektu, który ma typ T

public Przechowaj(T el)    // konstruktor
{
this.el = el;       //set
}

public T dajEl();
{
return el;             //get
}


Przykład zastosowania powyższej klasy:

public class Przyklad
{
public static void main(String args[]) 
{

Przechowaj<Integer> przech = new Przechowaj<Integer>(15);
Przechowaj<String> przech2 = new Przechowaj<String>("Przechowuje stringi");

int pobrana = przech.dajEl();
System.out.println(pobrana);

System.out.print(przech.dajEl());
}
}

Refleksja w javie

Refleksja pozwala na traktowania kodu podobnie do zwykłych danych. Pozwala to na zmianę zachowania funkcji lub metod, które są już zdefiniowane, w trakcie działania. Ponadto umożliwia pozyskanie informacji o klasach podczas wykonywania programu. W przypadku javy, opartej o maszynę wirtualną jest to czesto spotykany mechanizm.

Przykład:

Poniższy kod wykonany bez refleksji:

Klasa klasa = new Klasa();
klasa.doSth();

W przykładzie tego kodu z refleksją, przeniesiemy nazwę klasy i metody do zmiennych, których wartość można ustalić w czasie wykonywania programu.

Class jeden = Class.forName("Klasa");
 Method refleks = jeden.getMethod("doSth", null);
 refleks.invoke(jeden.newInstance(), null);
Drugi przykład pokaże jak za pomocą refleksji można zdobyć 
przydatne informacje o klasie podczas wykonywania programu:
 
Trzeba pamiętać o zaimportowaniu 
java.lang.reflect.*;
public class Dwa {
    public String funkcja(Integer i) {
        return "Odliczam:" + i";
    }
}
 
import static java.lang.System.out;
import java.lang.reflect.*;
 
public class Main 
{
    public static void main(String[] args) throws Exception  
{
  String className = "Dwa";
  Class p = Class.forName(className);  //dla danej klasy
  Method[] b = p.getDeclaredMethods();  //pobiera nazwy metod
  for (int i=0;i<b.length;++i)           // dla liczby metod w danej klasie
{ 
 out.print("Klasa " + className + " ma metode '" + b[i].getName().toString() + "'");
 out.println(" ktora zwraca wartosc typu " + b[i].getReturnType().toString());
        }
    }
}

Kohezja i coupling

 Kohezja

Pojęcie stopnia kohezji określa jak szeroką funkcjonalność ma klasa. Im wyższy stopień kohezji tym klasa obejmuje węższy zakres różnorodnych funkcji. Zaleca się stosowanie jak najwyższego stopnia kohezji aby łatwiej operować na kodzie oraz go modyfikować. Łatwiej przystosowywać projekt do określonych wzorców projektowych.

Coupling

Coupling jest swoistym zabezpieczeniem, które zapewnia ograniczony dostęp do składowych klas. Umożliwia nam to ustawienie setterów i getterów tam gdzie pola klasy są oznaczone np. jako prywatne. Coupling powinno stosować się zawsze, aby uchronić się od niepowołanych zmian.

Przykładowo

aby uzyskać dostęp do zmiennej 

private zm;

musimy utworzyć metodę

getZm()
{
     return zm;
}

setZm(zm)
{
     this.zm = zm;
}