The Clean Architecture

Prezentacja wujka Boba na temat Clean Architecture

Główne założenia Clean Architecture

  • Struktura projektu powinien odzwierciedlać architekturę, a nie użyty framework (Screaming Architecture).
  • Elementy takie jak UI, serwer, baza danych, użyty ORM to szczegóły który powinny być ukryte
  • Testowalność – reguły binzesowe można testować nieleżenie od UI, użytego serwera, bazy danych itd.
  • Wykorzystanie Dependency Inversion principle

Więcej informacji:

Effective software delivery

Opis z YouTube: Podczas tej prezentacji przejdziemy przez poszczególne fazy dostarczania oprogramowania i odpowiemy sobie na pytanie jakie możliwości optymalizacji mamy do dyspozycji na poszczególnych etapach. Powiemy sobie jakich błędów unikać poczas prototypowania, jak wykorzystać specyfikę projektu i nie dać się ponieść pragnieniu rozwiązywania wszystkich problemów za pomocą złotego młotka. Poruszymy mit spowalniających development testów automatycznych, odpowiemy na pytanie czy DRY mówi tylko o unikaniu copy-paste w naszej aplikacji oraz przeanalizujemy szerzej zagadnienie przedwczesnej optymalizacji. Poznamy także narzędzia i techniki, która pozwalają nam efektywniej pracować nad wytwarzaniem oprogramowania.

Zasady dobrego programisty

Don’t Repeat Yourself (DRY) – nie powtarzaj się

Jest to jedna z najważniejszych zasad i powinniśmy traktować ją bardzo poważnie. Objawia się ona nie tylko powielaniem tego samego kodu i używanie metody Copy&Paste.

Single-Responsibility Principle (SRP) – zasada pojedynczej odpowiedzialności

Żadna klasa nie może być modyfikowana z więcej niż jednego powodu.

Jeśli jesteśmy w stanie wyobrazi sobie więcej niż jeden bodziec skłaniający programistę do zmiany klasy, mamy do czynienia z klasą rozciągającą się na więcej niż jeden obszar odpowiedzialności.*

Open/Close Principle (OCP) – zasada otwarte-zamknięte

Składniki oprogramowania (klasy, moduły, funkcje itp.) powinny być otwarte na rozbudowę, ale zamknięte dla modyfikacji.

Na początku może wydawać się to sprzeczne bo jak mamy wprowadzać zmiany skoro nie możemy modyfikować kodu. Odpowiedzią jest abstrakcja. W świecie obiektowym abstrakcje mają postać abstrakcyjnych klas bazowych, a grupa niezwiązanych, możliwych zastosowań jest reprezentowana przez klasy pochodne.
Moduł, który wykorzystuje abstrakcje, może być zamknięty dla modyfikacji, ponieważ jego działanie jest uzależnione właśnie od tej abstrakcji. Dzięki temu mamy możliwość rozszerzania tego modułu bez potrzeby modyfikacji jego kodu.

Liskov Substitution Principle (LSP) – zasada podstawień Liskov

Musi istnieć możliwość zastępowania typów bazowych ich podtypami.

Znaczenie tej zasady staje się oczywiste, kiedy rozważymy konsekwencje jej naruszenia. Przypuśćmy, że dysponujemy funkcją f, która otrzymuje za pośrednictwem swojego parametru referencję do pewnej klasy bazowej B. Załóżmy także, że przekazanie na wejściu funkcji referencji do klasy D (potomnej względem klasy B) powoduje błędne działanie tej funkcji. W takim przypadku klasa D narusza zasadę podstawienia Liskov (LSP). Oznacza to, że klasa D jest wrażliwa w kontekście funkcji f.
Prostym przykładem naruszenia tej zasady jest weryfikacja typów za pomocą instrukcji if-else

Dependecy-Inversion Principle (DIP) – zasada odwracania zależności

Moduły wysokopoziomowe nie powinny zależeć od modułów niskopoziomowych. Obie grupy modułów powinny zależeć od abstrakcji.

Abstrakcje nie powinny zależeć od szczegółowych rozwiązań. To szczegółowe rozwiązania powinny zależeć od abstrakcji.

Interface Segregation Principle (ISP) – zasada segregacji interfejsów

Ma na celu wyeliminowanie nieporęcznych “grubych” interfejsów. Do tej grupy zaliczamy nadmiernie rozbudowane i niespójne interfejsy klas. Innymi słowy, interfejsy takich klas należy dzielić na mniejsze grupy metod. Każda taka grupa odpowiada za obsługę innego zbioru klientów. Oznacza to, że część klientów będzie korzystała z jednej grypy metod, podczas gdy pozostałe aplikacje klienckie będą korzystały z pozostałych grup metod.

Zasada skautów: Pozostaw obóz czyściejszym, niż go zastałeś.

(Staraj się pozostawić ten świat nieco lepszym niż go zastałeś)

*Stosowanie reguł jest błędem, jeśli nie znajdujemy uzasadnienia w sytuacji faktycznej. O osi zmian możemy mówić wtedy i tylko wtedy, gdy odpowiednie zmiany mają miejsce.

Bibliografia:

  • Agile Programowanie zminne, Robert C. Martin, Micah Martin
  • Czysty kod, Robert C. Martin

The transaction log for db BizTalkMsgBoxDb is full.

SQL Server returned error string: “The transaction log for database ‘BizTalkMsgBoxDb’ is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.databases”.

Solution:

[sql]

sp_helpdb ‘BizTalkMsgBoxDb’

ALTER DATABASE BiztalkMsgBoxDb

SET RECOVERY SIMPLE;

GO

DBCC SHRINKFILE (BiztalkMsgBoxDb_log, 1);

GO

sp_helpdb ‘BizTalkMsgBoxDb’

GO

ALTER DATABASE BiztalkMsgBoxDb

SET RECOVERY FULL

GO

[/sql]

MSCRM 4.0 Web Service Toolkit (JavaScript)

 CRM Web Service Toolkit jest małą JavaScript’ową biblioteką umożliwiającą łatwy dostęp do web serwisów Microsoft Dynamics CRM 4.0.
Dzięki niej możemy w szybki sposób:

  • tworzć/aktualizować encje
  • zmieniać stan encji
  • tworzyć zapytania za pomocą fetchXML czy poprzez metodę RetrieveMultiple
  • i wiele innych metod dostępnych w web serwisach CRMa

Przykłady

Encje tworzymy za pomocą metody CrmServiceToolkit.Create()

[js]

// Use CrmServiceToolkit. Create() to create a CRM contact record.
var contact = new CrmServiceToolkit.BusinessEntity("contact");
contact.attributes["firstname"] = "Diane";
contact.attributes["lastname"] = "Morgan";
contact.attributes["gendercode"] = 2;
contact.attributes["familystatuscode"] = 1; // Picklist : Single – 1
contact.attributes["creditlimit"] = 3000;

var createResponse = CrmServiceToolkit.Create(contact);

[/js]

CrmServiceToolkit.Retrieve() używamy do zwracania pojedynczych encji

[js]
// Use CrmServiceToolkit.Retrieve() to retrieve a CRM contact record.
var contactId = ‘3210F2BC-1630-EB11-8AB1-0003AAA0123C’;
var cols = ["firstname", "lastname", "familystatuscode", "creditlimit", "birthdate", "donotemail"];
var retrievedContact = CrmServiceToolkit.Retrieve("contact", contactId, cols);

alert(retrievedContact.getValue(‘lastname’));
alert(retrievedContact.getValue(‘firstname’));
// Picklist value (integer)
alert(retrievedContact.getValue(‘familystatuscode’));
// Picklist selected text
alert(retrievedContact.getValue(‘familystatuscode’, ‘name’));
// Currency field value
alert(retrievedContact.getValue(‘creditlimit’));
// Currency field formatted value (string)
alert(retrievedContact.getValue(‘creditlimit’, ‘formattedvalue’));
// Datetime field date/time value
alert(retrievedContact.getValue(‘birthdate’));
// Datetime field date string
alert(retrievedContact.getValue(‘birthdate’, ‘date’));
// Datetime field time string
alert(retrievedContact.getValue(‘birthdate’, ‘time’));
// Bit field value
alert(retrievedContact.getValueAsBoolean(‘donotemail’));
[/js]

Metoda CrmServiceToolkit.setState() umożliwia nam zmiane stany encji

[js]
// Use CrmServiceToolkit.setState() to update a CRM record status.
var contactId = ‘3210F2BC-1630-EB11-8AB1-0003AAA0123C’;
var response = CrmServiceToolkit.setState(‘contact’, contactId, ‘Inactive’, 2);
alert(response);
[/js]

CrmServiceToolkit.queryByAttribute() zwraca nam wszystkie rekordy spełniające podane kryterja.

[js]
// Use CrmServiceToolkit.queryByAttribute() to retrieve all CRM records that match the query criteria.
var queryOptions = {
entityName : "contact",
attributes : ["firstname", "lastname"], // Search by firstname and lastname
values : ["John", "Smith"], // Find all contacts whose firstname is John, lastname is Smith
columnSet : ["familystatuscode", "creditlimit", "birthdate"],
orderby : ["creditlimit", "birthdate"]
};

var fetchedContacts = CrmServiceToolkit.queryByAttribute(queryOptions);
[/js]

Więcej informacji na:

http://crmtoolkit.codeplex.com/

http://danielcai.blogspot.com/2010/07/crm-web-service-toolkit-for-javascript.html

MSCRM 4.0 Form Properties

Do pól formularza odwołujemy się poprzez obiekt crmForm:

[js]
crmForm.propertyname
[/js]

Tabelka poniżej przedstawia dostępne właściwości.

Właściwość Typ Opis
IsDirty bool Określa czy formularz był modyfikowany
FormType int Tylko do odczytu. Typ formularza.Możliwe wartości:

  • Undefined Form Type = 0
  • Create Form = 1
  • Update Form = 2
  • Read Only Form = 3
  • Disabled Form = 4
  • Quick Create Form = 5
  • Bulk Edit Form = 6
ObjectId string Tylko do odczytu. Identyfikator encji. Jeśli formularz jest w trybie edycji
ObjectTypeCode string Tylko do odczytu. Kod typu encji którą edytujemy/wyświetlamy na formularzu
ObjectTypeName string Tylko do odczytu.  Nazwa typu encji której dotyczy formularz
all.{field name}  Kolekcja poszczególnych pól formularza odpowiadającym polom w encji lub IFRAME formularza.

Przykład

[js]

var CRM_FORM_TYPE_CREATE = 1;
var CRM_FORM_TYPE_UPDATE = 2;

// Test the FormType and ObjectId properties.
switch (crmForm.FormType)
{
case CRM_FORM_TYPE_CREATE:
alert("This is a create form.");
break;

case CRM_FORM_TYPE_UPDATE:
alert("This is an update form, the ID is " + crmForm.ObjectId + ".");
break;
}
[/js]
[js]
var CRM_ENTITY_ACCOUNT = 1;
var CRM_ENTITY_CONTACT = 2;

// Test the ObjectTypeCode property.
switch (crmForm.ObjectTypeCode)
{
case CRM_ENTITY_ACCOUNT:
alert("This is an Account form.");
break;

case CRM_ENTITY_CONTACT:
alert("This is a Contact form.");
break;
}

[/js]

Żródło: http://msdn.microsoft.com/en-us/library/cc150873.aspx

Testy jednostkowe: testowanie zdarzeń

Ostatni natknąłem się na problem testowania zdarzeń. Sprawa niestety nie jest prosta na pierwszy rzut oka. Zdarzenia w aplikacji wywołują się asynchronicznie (nie wiemy dokładnie kiedy zdarzenie zajdzie) więc aby je przetestować musimy poczekać aż dane zdarzenie wystąpi i wtedy zweryfikować poprawność danych. Tak więc nasuwa się pytanie jak wstrzymać metodę testową aby czekała do momentu zajścia zdarzenia lub jeśli jest to wymagane zasygnalizowała nam, że czas na wykonanie zadania minął.

Bardzo przydatna w takich przypadkach okazuje się klasa Monitor i instrukcja lock. Cały mechanizm opiera się na zablokowaniu obiektu w metodzie testującej za pomocą metody Monitor.Wait i czekanie na odblokowanie go przez zdarzenie metoda Monitor.Pulse. Taka funkcjonalność w zupełności wystarcza na potrzeby testów jednostkowy, a prostota sprawia, że testy tworzymy w szybki sposób. Przyjdźmy do przykładu i zobaczmy jak to wszystko wygląda w praktyce.

[csharp]

[TestMethod()]
public void MyExampleEventTest()
{
MyApplication target = new MyApplication();
bool result = false;
target.TestEvent += (s, e) =>
{
//Do something
result = s is MyApplication
lock (this)
{
Monitor.Pulse(this);
}
};
int timeout = 1000;
lock (this)
{
if (!Monitor.Wait(this,timeout)) //Wait for Event
Assert.Fail("Time out");

}
Assert.IsTrue(result);

}
[/csharp]

W przedstawionym przykładzie wykorzystałem funkcję Wait która po określonym czasie kończy działanie. Oczywiście możemy użyć też innej wersji funkcji Wait np. bez podawania czasu, dzięki czemu metoda testujące będzie czekać do skutku czyli do momentu wystąpienia zdarzenia.

Jawna i niejawna implementacja interfejsu w C#

Interfejs klasy — w obiektowych językach programowania interfejs jest abstrakcyjną reprezentacją klasy pozwalającą na korzystanie z niej niezależnie od faktycznej implementacji. Interfejs pozwala na enkapsulację wielu różnych obiektów utworzonych w oparciu o odmienne klasy, które zawierają implementację wspólnego interfejsu. [wikipedia]
Interfejs dostarcza nam pewną funkcjonalność którą realizują klasy implementujące go, dzięki czemu możemy posługiwać się wyłącznie interfejsem aby zrealizować konkretne zadania bez konieczności zagłębiania się w kod znajdujący się za nimi.

Interfejsy w C#

W języku C# definiując interfejs posługujemy się słowem kluczowym interface, trzeba pamiętać że przy tworzeniu interfejsu nie używamy modyfikatorów dostępu, gdyż wszystkie elementy zawarte w interfejsie są publiczne. W interfejsach tworzymy jedynie definicje wybranych elementów które muszą zaimplementować klasy/struktury je implementujące.

Interfejsy mogą zawierać deklarację:

  • metod
  • właściwości
  • zdarzeń
  • indekserów

Interfejsy nie mogą zawierać stałych, pól, operatorów, konstruktorów, destruktorów i zagnieżdżonych typów. Nie mogą zawierać również statycznych elementów.

Interfejsy mogą dziedziczyć po jednym lub kilku interfejsach. Elementami implementującymi interfejsy są klasy lub struktury (mogą on implementować wiele interfejsów). W języku C# mamy również możliwość tworzenia generycznych interfejsów.

Niejawna implementacja interfejsu

Niejawna implementacja interfejsu jest najbardziej znaną formą implementacji. Polega ona na  zaimplementowaniu publicznych elementów których definicje zawiera interfejs:

[csharp]
interface ISampleInterface1
{
void Print();
string Message { get; }
}
interface ISampleInterface2
{
void Print();
}
class SampleClass : ISampleInterface1, ISampleInterface2
{
// Zarówno ISampleInterface1.Print i ISampleInterface2.Print
// wywołają tą metodę
public void Print()
{
Console.WriteLine("Metoda Print");
}
private string _message = "";
public string Message
{
get { return _message; }
set { _message = value; }
}
}
[/csharp]

Jak widzimy nie ma tu żadnej magi implementujemy w klasie SampleClass metodę Print i właściwość Message z interfejsu ISampleInterface1 do której dopisujemy setter.

Jawna implementacja interfejsu

Aby jawnie zaimplementować interfejs musimy nazwę implementowanego elementu poprzedzić nazwą interfejsu:

[csharp]
class SampleClass : ISampleInterface1, ISampleInterface2
{
void ISampleInterface1.Print()
{
Console.WriteLine("ISampleInterface1.Print");
}

void ISampleInterface2.Print()
{
Console.WriteLine("ISampleInterface2.Print");
}

private string _message = "";
string ISampleInterface1.Message
{
get { return _message; }
}
}
[/csharp]

Dzięki jawnej implementacji mamy możliwość zaimplementowani dwóch metod o tej samej nazwie co czasami może być niezmiernie przydatne. Przy jawnej implementacji trzeba pamiętać, że nie możemy używać modyfikatorów dostępu. Z jawną implementacją wiąże się też kilka niedogodności: implementacje elementów interfejsu nie są widoczne w klasie implementujące, aby uzyskać do nich dostęp musimy posługiwać się konkretny interfejsem. W przypadku właściwości Message nie możemy dopisać settera gdyż dostaniemy błąd kompilacji.

Więcej informacji na temat interfejsów można uzyskać w:
Interfaces (C# Programming Guide), Explicit Interface Implementation, Explicit Interface Implementation Tutorial, Implicit and Explicit Interface Implementations, Design Guideline Update: Explicit Member Implementation, Interface layout, Interface (C# Reference),