Wzorce: Odwiedzający / Wizytator

Tak mnie wzięło na wzorce projektowe ostatnimi czasy, dzisiaj coś o o odwiedzającym (wizytatorze). Polska Wikipedia mówi o nim tak odwiedzający.
A teraz moimi słowami:
Wzorzec umożliwia przejście po strukturze danych, oraz zebranie jakichś informacji. Gdy zaistnieje potrzeba zaimplementowania nowej funkcjonalności, gdzie pobieranie danych jest zaimplementowane tak samo, ale rodzaj danych będzie się różnić, problem ten rozwiąże się tworząc nową klasę “odwiedzającą” strukturę danych, która zbierze nowe informacje.
Gdyby zastosować taki wzorzec np. do parsowania XML, pierwszy odwiedzający zbiera informacje o wszystkich linkach, które znajdują się w przeglądanym XML. Inna implementacja zlicza po prostu, wszystkie elementy, czy atrybuty takiego XML. W każdym przypadku, następuje przejście po całej strukturze, różnica jest natomiast w wyniku takiej operacji. Na tym właśnie polega “odseparowanie algorytmu od struktury obiektowej na której się operuje”.


Przykładowy kod:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3.  
  4. namespace Visitor
  5. {
  6.     class SimpleExample
  7.     {
  8.         internal interface IElement
  9.         {
  10.             void Accept(IVisitor aVisitor);
  11.         }
  12.  
  13.         internal interface IVisitor
  14.         {
  15.             void Visit(IElement aElement);
  16.         }
  17.  
  18.         public class ElementClassA : IElement
  19.         {
  20.             public void Accept(IVisitor aVisitor)
  21.             {
  22.                 aVisitor.Visit(this);
  23.             }
  24.         }
  25.         public class ElementClassB : IElement
  26.         {
  27.             public void Accept(IVisitor aVisitor)
  28.             {
  29.                 aVisitor.Visit(this);
  30.             }
  31.         }
  32.  
  33.         public class ELementClassC : ElementClassB
  34.         {
  35.             new public void Accept(IVisitor aVisitor)
  36.             {
  37.                 aVisitor.Visit(this);
  38.             }
  39.         }
  40.  
  41.         public class RootElement : IElement
  42.         {
  43.             private ElementClassA _elementA;
  44.             private List<ElementClassB> _elementsBList;
  45.             private static ELementClassC _elementc = new ELementClassC();
  46.             public void Accept(IVisitor aVisitor)
  47.             {
  48.                 aVisitor.Visit(this);
  49.                 _elementA.Accept(aVisitor);
  50.                 foreach (var elementClassB in _elementsBList)
  51.                 {
  52.                     elementClassB.Accept(aVisitor);
  53.                 }
  54.                 RootElement._elementc.Accept(aVisitor);
  55.             }
  56.  
  57.             public RootElement()
  58.             {
  59.                 _elementA = new ElementClassA();
  60.                 _elementsBList = new List<ElementClassB>();
  61.                 for (int i = 0; i < 10; ++i)
  62.                 {
  63.                     _elementsBList.Add(new ElementClassB());
  64.                 }
  65.  
  66.             }
  67.         }
  68.  
  69.         public class VisitorToString : IVisitor
  70.         {
  71.             public int Count { get; set; }
  72.             public VisitorToString()
  73.             {
  74.                 Count = 0;
  75.             }
  76.  
  77.             public void Visit(IElement aElement)
  78.             {
  79.                 string s = string.Format(“Visited class {0}”, aElement.ToString());
  80.                 Count += s.Length;
  81.                 Console.WriteLine(s);
  82.             }
  83.         }
  84.  
  85.         public class VisitorHashString : IVisitor
  86.         {
  87.             public int Count { get; set; }
  88.             public VisitorHashString()
  89.             {
  90.                 Count = 0;
  91.             }
  92.             public void Visit(IElement aElement)
  93.             {
  94.                 string s = string.Format(“Visited class {0}, hash code {1}”, aElement.ToString(), aElement.GetHashCode());
  95.                 Count++;
  96.                 Console.WriteLine(s);
  97.             }
  98.         }
  99.         
  100.         public static void Main()
  101.         {
  102.             RootElement root = new RootElement();
  103.             VisitorToString vstring = new VisitorToString();
  104.             VisitorHashString vhstring = new VisitorHashString();
  105.             root.Accept(vstring);
  106.             Console.WriteLine(“Total written {0}”,vstring.Count);
  107.             root.Accept(vhstring);
  108.             Console.WriteLine(“Total written {0}”,vhstring.Count);
  109.         }
  110.     }
  111. }

W przykładzie jest dwóch odwiedzających. Jeden zlicza wszystkie literki, które zostały wykorzystane do wyświetlenia informacji o odwiedzanych elementach. Drugi zbiera informację o ilości odwiedzonych elementów. Oba wypisują jakieś tam informacje o odwiedzonych elementach, można to zignorować.
W przykładnie, klasy dziedziczące po IElement nie wykonują zbyt wiele poza akceptowaniem odwiedzającego, czy wysłaniem go do wszystkich swoich pól. IElement nie definiuje żadnej funkcjonalności, z której skorzystać mógłbym odwiedzający. Implementację odwiedzającego warto uodpornić na zmiany kolejności wywołać Accept (klasa agregująca), odwiedzający powinien poprawnie zadziałać bez względu na to czy przykładowy RootElement najpierw wyśle do odwiedzającego siebie, czy wcześniej przejdzie po wszystkich swoich polach i dopiero potem sama podda się odwiedzinom. Dlaczego warto tego przypilnować? Ponieważ nie zawsze mamy kontrolę na implementacją odwiedzanych elementów, np. zewnętrzna biblioteka. W nowszej wersji może się okazać, że zmieniono kolejność wywołania Accept ze względu na zwiększenie wydajności albo dodano nowe pola w odwiedzanej klasie.
Warto również zwrócić uwagę, że odwiedzający przyjmuje interfejs, nie będzie więc on w stanie wykonać innych operacji niż tam zdefiniowane. Można to rozwiązać na co najmniej dwa sposoby:
– Bardzo dobrze zdefiniować taki interfejs
– Dodać kolejne metody przeciążające metodę Accept, gdzie parametrem będą klasy na których odwiedzeniu nam zależy

Poniżej rozwiązanie drugie, troszkę rozbudowane:

Code Snippet
  1. using System;
  2.  
  3. namespace Visitor
  4. {
  5.     public interface IVisitor
  6.     {
  7.         void HandleVisit(IObject aObject);
  8.     }
  9.  
  10.     public interface IObject
  11.     {
  12.         void AcceptVisitor(IVisitor aVisitor);
  13.     }
  14.  
  15.     class GenericVisitor : IVisitor
  16.     {
  17.         void IVisitor.HandleVisit(IObject aObject)
  18.         {
  19.             throw new ArgumentException(
  20.                 string.Format(
  21.                     “Sorry but this visitor accepts only ConcreteObjectB types class. Please fix your code!. Error generated by instance of {0} class”,
  22.                     aObject));
  23.         }
  24.  
  25.         public void HandleVisit( ConcreteObjectB aObjectB)
  26.         {
  27.             Console.WriteLine(“Handle visit of class {0}”, aObjectB);
  28.         }
  29.     }
  30.  
  31.     class StronglyTypedVisitor : IVisitor
  32.     {
  33.         void IVisitor.HandleVisit(IObject aObject)
  34.         {
  35.             throw new ArgumentException(string.Format(“Just found new not properly handled visitor {0}- fix it”, aObject));
  36.         }
  37.  
  38.         public void HandleVisit(ConcreteObjectA aObject)
  39.         {
  40.             Console.WriteLine(“Visited by a strongly typed ObjectA instance”);
  41.         }
  42.  
  43.         public void HandleVisit(ConcreteObjectB aObject)
  44.         {
  45.             Console.WriteLine(“Visited by a strongly typed ObjectB instance”);
  46.         }
  47.     }
  48.  
  49.     class ConcreteObjectA : IObject
  50.     {
  51.         void IObject.AcceptVisitor(IVisitor aVisitor)
  52.         {
  53.             throw new ArgumentException(
  54.                 string.Format(
  55.                     “This object only accepts strongly typed objects. Fix your code. Class that generated exception {0}”,
  56.                     aVisitor));
  57.         }
  58.  
  59.         public void AcceptVisitor(StronglyTypedVisitor aVisitor)
  60.         {
  61.             aVisitor.HandleVisit(this);
  62.         }
  63.     }
  64.  
  65.     class StronglyTypedConcreteObject: IObject
  66.     {
  67.         public void AcceptVisitor(IVisitor aVisitor)
  68.         {
  69.             throw new ArgumentException(string.Format(“Consider using strongly typed methodsn. Please implement this method: public void AcceptVisitor({0} aVisitor)”, aVisitor));
  70.         }
  71.     }
  72.  
  73.     class ConcreteObjectB : IObject
  74.     {
  75.         void IObject.AcceptVisitor(IVisitor aVisitor)
  76.         {
  77.             throw new ArgumentException(
  78.                 string.Format(
  79.                     “ConcreteObjectB class accepts only StronglyTypedVisitor, please fix your code. Exception caused by instance of {0} class”,
  80.                     aVisitor));
  81.         }
  82.  
  83.         public void AcceptVisitor(StronglyTypedVisitor aVisitor)
  84.         {
  85.             aVisitor.HandleVisit(this);
  86.         }
  87.     }
  88.  
  89.     class Program
  90.     {
  91.         static void Main()
  92.         {
  93.             ConcreteObjectA a = new ConcreteObjectA();
  94.             ConcreteObjectB b = new ConcreteObjectB();
  95.             StronglyTypedVisitor sv = new StronglyTypedVisitor();
  96.  
  97.             //proper usage of code
  98.             a.AcceptVisitor(sv);
  99.             b.AcceptVisitor(sv);
  100.  
  101.             //now lets try to do some tricks
  102.             GenericVisitor gv = new GenericVisitor();
  103.  
  104.             try
  105.             {
  106.                 //a.AcceptVisitor(gv); this will generate compile error
  107.                 (a as IObject).AcceptVisitor(gv);       //oooo Im so great!
  108.             }
  109.             catch (ArgumentException ae)
  110.             {
  111.                 Console.WriteLine(“—————————————————————————————n{0}”,ae);
  112.             }
  113.  
  114.  
  115.             //example with synchronization lost!
  116.             try
  117.             {
  118.                 //Im sure it was agreed that the GeneralVisitor accepts ConcreteB, maybe its just a bug, I will try with the interface
  119.                 //b.AcceptVisitor(gv); //this will generate compile error
  120.                 (b as IObject).AcceptVisitor(gv);       //looks fine to me and the compilator, so it works!
  121.             }
  122.             catch (ArgumentException ae)
  123.             {
  124.                 Console.WriteLine(“—————————————————————————————n{0}”, ae);
  125.             }
  126.  
  127.         }
  128.     }
  129. }

C# oferuje możliwość zasłonięcia metody dziedziczonej po interfejsie, poprzez jej jawną implementacje (@17), oraz przeciążenie jej inną publiczną metodą, którą zadowoli kompilator (@25).
Wykorzystuje to do poinformowania użytkowników klas, że np. nie obsługuje ona innych typów klas, niż te dostępne publicznie (@19).
Skorzystałem z wyjątków, ze względu na czytelność kodu i przekazu dla użytkownika moich przykładowych klas. Innym sposobem jest np. zastosowanie asercji, które zadziałają jak należy, a w przypadku gdy aplikacja nie została dobrze przetestowana, nie wywalą wyjątku podczas oddawania aplikacji klientowi.
Niektórym może się nie spodobać takie przesłanianie metod, powiedzą że naruszona została zasada kontraktów i obiektowości. OK, ale! Zyskuje się na tym, wcześniejsze wykrywanie nie poprawnego korzystania z interfejsu. W dokumentacji można dodać odpowienie adnotacje, że dana implementacja odwiedzającego działa tylko z daną listą klas.
W części gdzie nie używam try/catch widać, że wszystko działa poprawnie, korzysta się z klas tak, jak osoba je pisząca sobie przewidziała. Nie potrzeba żadnych czarów, rzutowania etc. Wynikiem czego jest też poprawne działanie kodu. To akurat nie jest ciekawe.
Użytkownik decydując się na rzutowanie obiektów, korzysta w nie cywilizowany (@107, @120) sposób jest ciekawsza. Generowany jest czytelny komunikat i prośba o poprawienie kodu, oraz jedyny słuszny błąd w c# – wyjątek. I na koniec przykład z klasą GenericVisitor i ConcreteObjectB, widać jak można sobie rozsynchronizować kod. Co prawda przykład lekko naciągany, ale jednak.

Do czego się przydaje taki wzorzec, gdzie z niego skorzystać?
– Obiekty klas A i B można poddać walidacji, gdzie Wizytor będzie walidować ich poprawność. Warto też wtedy poprawniej nazwać poszczególne klasy.
– A i B zawierają jakieś zadania, które należy z nich wyciągnąć i zakolejkować, A pobiera informacje o zadaniach z sieciA a B pobiera z obserwowanego katalogu. Aplikacja tworzy odpowiednie klasy oczekujące na nowe zadanie, obiekty a lub b zgłaszają nadejście nowych zadań z odpowiednich źródeł, aplikacja przekazuje obiekt a lub b do obiektu zajmującego się zbieraniem takich zadań.
– A i B chcą uzyskać dostęp do strzeżonego obiektu, wcześniej muszą się zamedlować
u strażnika, aby sprawdzić czy mają odpowiednie uprawnienia dostępu
– Parsowanie skomplikowanych struktur

Bardziej życiowe porównanie, choć nie wiem czy najlepsze: organizujecie imprezę, wyklejacie plakaty: “Zapraszamy do nas na mega piwo”, przychodzą różne obiekty, klasa walidator przepuszcza część z nich, a innym grzecznie odmawia.

Cechy takiego wzorca:
– Trzeba napisać więcej kodu
– Może dojść do niesynchronizowania się klas odwiedzanych i odwiedzających, choć trzeba się o to porządnie postarać, dodałem go do przykładu (@120)
+ Czytelniejszy kod
+ Jasne określenie co jest wspierane przez klasy
+ Znika potrzeba rzutowania obiektów podczas ich obsługi
+ Jesteśmy zajebiści bo korzystamy ze wzorców projektowych

To tylko część z sytuacji, w których możliwe jest skorzystanie z tego wzorca. Jedynym ograniczeniem jest tutaj nasza wyobraźnia i zdrowy rozsądek.

JS

Wzorce: NVI / metoda szablonowa

Czytając mądrą książeczką o której pisałem tutaj, przeczytałem fragment o wzorcu NVI – Non Virtual Interface (nazywanym także metodą szablonową – polskie wiki[słaby art], wersja ang.).
Zamierzam się trzymać tej pierwszej nazwy, ponieważ pod taką nazwą ja go poznałem.
Cała idea polega na tym, że udostępnia się publiczną nie wirtualną metodę, z której może korzystać klient naszej (i potomnych) klasy.
Implementacja tej metody polega na wywołaniu kolejnej metody, która wykonuje właściwe zadanie. Metoda wykonująca właściwe zadanie oznaczona jest jako niepubliczna i wirtualna.
Klasy pochodne mogą i w zasadzie powinny ją przeciążać na własny użytek. Przykładowy kod można zmodyfikować tak, aby metoda była czysto wirtualna, co automatycznie wymusi jej implementacje w klasach potomnych. Ja pozostanę przy przykładzie z książki.

Jaki jest z tego pożytek?
Udostępniając metodę publiczną nie wirtualną, definiujemy jedyny dostęp do funkcjonalności na której zależy użytkownikowi, uniemożliwiamy zmianę tego domyślnego zachowania w klasach pochodnych (przecież nie jest wirtualna). I co najważniejsze (IMO) wszelkie zmiany następują w klasie bazowej, wpływając na wszystkie klasy potomne. A więc bugfix, dodatkowe logowanie, try/catch wszystkiego tego dokonujemy tylko w jednym miejscu. Czy to nie śliczne?

Oczywista oczywistość, jeżeli ktoś będzie chciał nadpisać metodę to ją nadpisze. Wzorce nie są idioto odporne.

Przykładowy kod c++

#include  <iostream> 
using  namespace  std;

class  A
{
public :
 void  DoWork()
 {
  //I can add some code here to bug fix something 
  DoCoreWork();
  //also I can add some log/comment here - all changes will affect me and my inheritace 
 }

private :
 virtual  void  DoCoreWork()
 {
  cout<<"Actually doing some work here!n" ;
 }
};

class  B : public  A
{
 virtual  void  DoCoreWork()
 {
  cout<<"Actually I refuse to workn" ;
 }
};

int  _tmain(int  argc, _TCHAR* argv[])
{
 A* a1 = new  A();
 A* a2 = new  B();

 a1->DoWork();
 a2->DoWork();

 return  0;
}

Oraz przykład dla c#

1: using  System;
2: namespace  Blog
3: {
4:     public  class  A 
5:     {
6:         public  void  DoWork()
7:         {
8:             //The same situation as befere. All fixes and updates can be put here 
9:             CoreDoWork();
10:             //Or here it depends on the case. 
11:         }
12:         protected  virtual  void  CoreDoWork()
13:         {
14:             Console .WriteLine("Actually doing some work" );
15:         }
16:     }
17:     public  class  B  : A 
18:     {
19:         protected  override  void  CoreDoWork()
20:         {
21:             Console .WriteLine("Refuse to work at all" );
22:         }
23:     }
24:     public  class  Program 
25:     {
26:         static  void  Main()
27:         {
28:             A  a1 = new  A ();
29:             A  a2 = new  B ();
30:             a1.DoWork();
31:             a2.DoWork();
32:         }
33:     }
34: }
35: 

Na koniec dodam, że wszelkie podobieństwo kodu do tego w książce nie jest przypadkowe, zostało on z niej zaczerpnięty.

Co o tym sądzicie? Wykorzystacie to kiedyś? Uważacie że jest do bani? Gdzieś się pomyliłem? Macie inne zdanie? Jak zawsze można śmiało komentować.

JS

Git podstawy

Ten wpis jest podsumowaniem wiedzy, którą zdobywałem czytając wprowadzenie do gita na stronie gitref.org. Jest to w zasadzie kurs który można tam przeczytać, tyle że przetłumaczony na język polski.
Instalowanie kluczy zostało praktycznie przeklejone ze strony eastgroup.pl. Wszelkie uwagi do tłumaczenia, błędów w skrypcie, uwag stylistycznych, merytorycznych i innych złośliwości należą się mnie, brawa i słowa gratulacji oryginalnym twórcom kursu.
O tym czym jest git można przeczytać na pl.wikipedia.org. Aby móc skorzystać z poleceń, które są tutaj wymieniane należy zainstalować gita, można go pobrać z strony http://git-scm.com/ klikając w odpowiedni system operacyjny. Ja skorzystałem z Git-1.7.4-preview20110204.exe dla windows.
Do dzieła!

Git podstawy:

  • Tworzenie repozytorium git w aktualnym katalogu: git init
  • Klon repozytorium ze zdalnego URL git clone url, wszystkie informacje na temat repozytorium zostają sklonowane. Git utworzy nowy katalog o nazwie projektu z którego został stworzony klon. Lokalna kopia posiada pełną historię zmian.
  • Status plików w aktualnym katalogu git status (-s krótszy opis). Wynikiem takiej operacji będzie lista plików z przypisanymi do nich atrybutami, poniżej znaczenie tych atrybutów:
    ? – nie zawierają się w repo
    A
    – dodany
    M
    -zmodyfikowany
    D
    – plik usunięty
    U
    – plik zaktualizowany ale nie zmerdżowany
    C
    – plik skopiowany
    R
    – plik przeniesiony
    Jeżeli nic nie zostanie wyświetlone, oznacza to że żadnej z plików nie został zmodyfikowany, a więc nie ma w ogóle o czym mówić. Krótki status (-s) zawiera informacje o plikach podzieloną na dwie kolumny; 1- wartość względem lokalnego repozytorium, 2- wartość dla aktualnie wprowadzanych zmian. Warto wspomnieć, że plik zmodyfikowany ale nie dodany do tworzonego snapshota nie będzie wykorzystany w trakcie wykonywania polecenia git commit (o tym rozkazie niżej). Status takiego pliku to ? albo M.
  • Dodanie nowych plików do snapshota lub zatwierdzanie zmian w plikach, dla których już jest prowadzony index w repozytorium to git add filename, można podać więcej niż jeden plik jako parametr. Dodanie wszystkich plików z aktualnego katalogu git add *. Dodanie wszystkich plików i wszystkich podkatalogów git add .(to jest kropka jako parametr).
  • Sprawdzenie aktualnych zmian wprowadzonych lokalnych plikach  git diff. 
  • Zmiany snapshot względem repozytorium to git diff –cached.
    Istnieje także opcja pominięcia snapshota i sprawdzenie zmian względem repozytorium, takim poleceniem git diff HEAD.
    Podsumowanie zmian, które zostały wprowadzone w plikach, ale bez szczegółów dotyczących tych zmian git diff –stat.
  • Wysłanie snapshota, w snapshot znajdą się tylko pliki zatwierdzone poleceniem git add (czyli posiadają status A), do repozytorium: wykonać polecenie git commit -m commenthere i snapshot zostaje zapisany do lokalnego repozytorium. Poleceni git commit potrafi także skorzystać z plików, które nie zostały dodane do snapshota, należy skorzystać z przełącznika a, wykonać git commit -am commenthere (uwaga: chyba nie działa na branchach).
    Pominięcie parametru m spowoduje, że git otworzy w domyślnym edytorze plików plik (coś mi się wydaje, że jednak zawsze jest to vim – INSERT, napisz komentarz, ESC, :wq, ENTER), gdzie należy podać komentarz do aktualnie tworzonego commita.
    Należy, a w zasadzie trzeba! podczas comitowania podawać opis wprowadzanych zmian – taka dygresja.
  • Jeżeli będzie potrzeba cofnąć plik ze stanu A do M (z jakiegoś powodu, nie chcemy zapisywać zmian), należy użyć polecenia git reset HEAD — filename (po — jest spacja), po takiej operacji zawartość pliku nie zostanie zmieniona, ale zostanie on uznany za lokalnie nie zatwierdzony, co spowoduje jego domyślne pominięcie przy wykonaniu operacji git commit.
    Git rozróżnia kolejność parametrów, poleceni git commit –am nie jest równe git commit –ma – warto o tym pamiętać.
  • Usunięcie pliku z repozytorium oraz z dysku git rm filename, jeżeli z jakiegoś powodu plik należy usunąć tylko z git, ale pozostawić go na dysku warto dodać parametr cached, polecenie wyglądać będzie tak git rm –cached filename. Równie dobrze można po prostu usunąć plik samodzielnie z katalogu, a następnie potwierdzić zmiany poprzez git add.. 

Tworzenie nowych gałęzi dla projektu oraz ich łączenie (merge)
Czasem pojawia się potrzeba zrobienia czegoś na boku, w gitcie możemy to osiągnąć za pomocą polecenia git branch oraz git checkout.

  • git branch wyświetli listę wszystkich dostępnych rozgałęzień w projekcie, gdy poda się nazwę nowej gałęzi zostanie ona utworzona: git branch nowy. Wywołanie git branch -d nazwa spowoduje jej usunięcie. Warunkiem jest to, że zmiany zostały już zaakceptowane oraz połączone z główną gałęzią lub ich nie wprowadzono. Można także wymusić usunięcie poprzez krzyknięcie, czyli -D
  • Po stworzeniu gałęzi, aby na niej pracować należy się na nią przełączyć, służy do tego polecenie git checkout nazwa, od tej pory pracujemy na nowej gałęzi projektu, wszelkie zmiany będą widoczne tylko gdy będzie ona aktywna. Dla leniwych istenieje polecenie git checkout -b nazwa, który tworzy nowy gałąź i automatycznie się na nią przełącza.

Pracując na boku korzysta się oczywiście z poleceń git add i git commit, a po wszystkim gdy trzeba połączyć gałęzie z pomocą przychodzi git merge nazwa. Git scala obie gałęzie projektowe, wraz z historiami obu. Oczywiście może pojawić się konfilikt podczas tej operacji, użytkownik zostanie o tym poinformowany plik zostaną złączone a odpowiednie wstawki z narzędzia merdżującego znajdą sie w plikach (>>>>> oraz <<<<<). Po wywołaniu git status -s pliki, które są w konfilikcie i wymagają ingerencji programisty będą miec status U, należy wtedy ręcznie dokonać operacji łączenia plików, oraz zaakceptować wynik poprzez git add plik a następnie git commit -m pamiętając o komentarzu.

  • Dla wścibskich historyków powstało polecenie git log wyświetlające historię comitów do repo. Warto sprawdzić użycie przełączników git log [–oneline] [–graph] [–decorate]. Można stosować je razem i osobno, kwestia gustu. 
  • Wspierane są także labelki (tag) git tag -a nazwa_labelki.

Praca ze zdalnym repozytorium
Wszystkie operacja, które zostały do tej pory opisany, miały charakter lokalny. Nikt nie miał do nich dostępu, źródła było tworzone i modyfikowane wyłącznie osoby korzystające z tego komputera. Czas więc udostępnić nasze poprawki i złote myśli innym, niech podziwiają naszą wiedzę i kunszt programistyczny. Aby tego dokonać potrzeba będzie postawić server git, a tym zdecydowanie kiedy indziej. Zakładam że skorzysta się z dostępnych gdzieś serwerów np. github.com i sklonuje sie repozytorium do siebie na dysk.
Oto link do mojego przykładowego repozytorium, które zawierać powinno plik c, c++, czy c# wyświetlające hello world. Także można z niego śmiało skorzystać, aby zrobić klona git://github.com/jsthedeveloper/hello-world-blog-example.git

  • Sklonowanie projektu do siebie na dysk uzyskuje się za pomocą polecenia git clone url, gdzie url to zdalna ścieżka do repozytorium z którego chcemy skorzystać. Taka sklonowana wersja zawiera całą historię projektu, można ją obejrzeć za pomocą git log –oneline –graph –decorate (:q).
  • Wyświetlenie serwerów (aliasów) z których korzysta sklonowane repozytorium to git remote (parametr -v poda dokładne adresy). Dodanie nowego serwera to git remote add [alias] [url] i oczywiście usunięcie git remote rm alias
  • Gdy zaistnieje potrzeba aktualizacji lokalnych źródeł do najnowszych dostępnych na zdalnych repozytoriach można skorzystać z opcji git fetch [alias], jeżeli korzysta sie z więcej niż jednego serwera. Po takiej operacji może wystąpić potrzeba merga git merge. Git lubi leniwców, istnieje więc poleceni git pull, które jest połączeniem git fetch i git merge.
  • Potem następuje normalna praca (add/commit), jeżeli chce się wysłać zmiany na zdalne repozytorium należy użyć polecenia git push [alias] [branch]. Aby móc poprawnie wykonać pusha, należy się poprawie autoryzować w repo i mieć uprawnienia do zapisu w repo, co przekłada sie na posiadanie odpowiedniego linka do repo.

Generowanie kluczy
Jeżeli nie posiadamy kluczy rsa, możemy skorzystać z pomocy git-basha, który został zainstalowany na naszym komputerze, do wygenerowania odpowiedniej pary kluczy [RSA wikipedia]. Uruchamiamy Git Bash następnie wykonujemy poleceni ssh-keygen i idąc po najmniejszej linii opory na wszystko odpowiadamy enterem. Bash napisze gdzie zostały zapisane klucze, zawartość pliku z kluczem publicznym przeklejamy do serwera gitowskiego i od tej pory git push powienien działać.
To by było w sumie na tyle, jeśli chodzi o git w konsoli. Istnieją graficzne nakładki na git, jeżeli do instalacji został wykorzystany link, który podałem, to można od razu skorzystać z opcji git gui. Ten wpis jednak nie będzie tego omawiać.
Jeżeli po przeczytaniu będą pytania, proszę o kontakt. Jeżeli po przeczytaniu będą nudności, proszę załatwić sobie radyjko. Innego typu uwagi, proszę komentarz, mail, czy inna forme komunikacji.

JS

Początki raz jeszcze

Jest taka książka Accelerated C# 2010 którą postanowiłem przeczytać, aby utrwalić swoją wiedzę z C#. W polskiej księgarni można znaleźć za około 120 nowych polskich złotych.
Jest przyjaźnie napisana i tłumaczy w lekki (dla mnie) sposób C#. Jej dużym plusem jest to, że nie jest ona dla początkujących programistów, a raczej dla osób które posiadają wiedzę z C++/Java (czy innego języka) i teraz chcieli by się nauczyć szarpa. Dlaczego tak się zajarałem? Książki które czytałem wcześniej nie wspominały nic o destuktorach (ani o tym, że w zasadzie nie powinno się z nich korzystać) w C# czy konstruktorach statycznych.
Te statyczne konstruktory to dla mnie w ogóle faza, w javie też niby są, ja tam nie wiem, javą się nie interesowałem. Poza tym gostek (w sensie autor), pisze też trochę o ILDASM, jak to wygląda w środku i czego się nie powinno używać. Myślę sobie, że to dobra książka na powtórzenie podstawowych wiadomości o tym języku w wersji 4.0.
Przy okazji będę mógł, wreszcie uczciwie napisać z której książki nauczyłem się podstaw.

Słówka kluczowe:
Konstuktor statyczny
Destruktory
params (tego też nie znałem)

Uwagi do mojej wybrakowanej wiedzy, słownictwa i prostactwa proszę zamieszczać w komentarzach.
A korzystając z okazji zapraszam na:

Geeks on tour (spotkania trzech grup społecznościowych, związanych z technologiami Microsoft)
wrocnet (wrocławska grupa .net)

Czy się różni wróbel

Niby prosta sprawa, a tak długo jak się tego nie sprawdzi to nie poczuje się różnicy.
Czym się różni Process.Start od AppDomain.ExecuteAssembly – a tym, ze to pierwsze uruchomi proces niezależny od rodzica. Podczas gdy druga opcja będzie działać we w tym samym procesie, w tej samej konsoli.
Tyle na dziś.