Showing posts with label software craftsmanship. Show all posts
Showing posts with label software craftsmanship. Show all posts

Friday, May 30, 2014

The 7 Duties of Great Software Professionals. Really?

There are two reasons I am not a fan of this talk. First, once you saw Anthony Robbins' motivational speech you don't want to see anybody else. He is simply the best!

The second reason is that in this speech the author offers some challenges to developers, he suggest that setting and achieving professional goals is the way a developer should follow to become great.

Well, goals are very popular and well known self-motivation tools, but they sucks. Why?

Setting the Goals


Being driven by goals is endless race from one goal to the next one, and then when your first excitement is behind you, you may wonder: What was actually for?

As Paweł explains (in Polish) achieveing a goal is a desire of more dopamine in our brain. Every time you achieve a goal, you get a reward a in form of dopamine self-injection.
Unfortunately training yourself in achieving goals causes you get more dopamine b-e-f-o-r-e achieving and after that you get just only teeny-tiny injection. You feel not so good as you expected, so you want more dopamine. What you do then? Set next goal.

Desire of goals means constant staring at the difference between your current state (before ticking a goal off) and future state (after getting it done). This difference is Dis-Satisfaction.
However the delta form the structure above is called a resource needed to get the goal done, in fact this is the difference between two emotional states, two dopamine levels. This difference is felt as some discomfort.

The race form a goal to another one is driven by Dis-Satisfaction and the only way to avoid this feeling is more and more dopamine, more and more achieving.

Mindfulness

So let stop for a moment and try to see things as they are...
Mindfulness is a concept moment-by-moment awarness when you try to liberate yourself form the desire of achieving.

Following the mindfulness way you accept reality as it is and observe your relation to this reality. Obviously as human beings we need goals and we need achieve things. Starting it with awareness you achieve goals if needed, but you do it being awaken from the dopamine dream.

Hmmm...it was a little bit off-topic, but who cares...;)

Thursday, May 29, 2014

DDD from a Non-Java/C# Developer Perspective

During 4Developers I met some friends who are PHP and Perl developers. They were very interested in what kind of value Domain Driven Design may bring to them.

They found the topic very confusing because in their opinion speakers were strongly focused on Java and Object-Oriented programming. Those developers made connection between DDD and OO-programing especially in Java and C#. I think it's kind of misunderstanding.

Let's see some examples of what domain expert could say (credits to Sławek):

Domain expert says:

When a piece of art is viewed as a thumbnail by a customer, one is able to show interests in this piece of by adding it to the LightBox.

The system should generate the offer from the reservation.

An offer contains just only available products with current prices.

A customer is not obliged to accept the offer. One is able to accept or reject it as well.

and so forth...

So we have some domain scenarios in a very unstructured form as were expressed by the expert.

To build the right software we model a domain base on expert knowledge and domain scenarios.

And here I wanna to put strict distinction: the goal is to model the domain but using building blocks given by DDD community is just a way to achieve this goal in the Object-Oriented world.

In my opinion pure DDD modeling should be based on Domain-Specific Languages when we have language to directly express what the expert says and how the domain works. This programing style is even easier to apply with some script and loosely typed languages.

So, if you are non Java/C#/OO professional, please be sure you may get benefits with using DDD. You don't need building blocks in the form they are currently presented, because they are implementation of DDD in Object-Oriented context. The core is to model the domain and maintain pure isolated model in a form suitable to the technology you use.

Friday, May 2, 2014

InfoQ: *-Driven* do not change anything

InfoQ Team has published my aricle *-Driven* do not change anything.

I gave a speech on these theses during the 33rd Degree Conference 2013 and I was told I didn't know Domain Driven Desing at all. Well, I have my private opinion about this. But I am sure I know how the cognitive biases work and all that *Driven* madness is one of them.

This article is not intended to criticize anybody or anything. It is about priorities and searching for a balance. Being focused on our basic skills first, helps to use a *-Driven* approach properly especially in an unknown context. I do really appreciate all those mental frameworks because they do great job. But I think if software development is driven by something, these are stakeholder needs and our common sense. Being driven by anything else might be harmful simplification.

Enjoy the article!

Wednesday, December 18, 2013

The Developer and a developer

As an employer I have a privilege to decide who I work with. I hire people and also have fired some of them. I've noticed there is kind of employee who does right anything one has touched. And opposite: some people screw up even simple stupid tasks.

I cannot say what exactly is the difference, mostly I feel one is the right one. But I have some observations:
The Developer a developer
is focused on the task is focused on options of the solution or on 'the best solution'
is patient wants to have things NOW
when failed feels a frustration or a sadness when failed feels a guiltiness
assumes oneself of being a cause of one's mistakes assumes others of being a causes of one's mistakes
argue with people blame people
has own daily routine does things impulsively
wants to know what was done right or wrong afraids to know what was done right or wrong, so avoids it
takes things happened as a result of work and constant feedback takes things personally
takes the responsibility for a relationship between involved stakeholders takes the responsibility for tasks one was told to accomplish


Taking a responsibility

I always hear from team leaders: I want a developer to take a responsibility for assigned things. I was unable to understand what was 'a responsibility'. Now I think I should ask them: What are those 'things' you want a developer to take responsibility for?

Personally I am sure team leaders didn't mean responsibility for a task. A task is nothing. Having a task completed is not always directly related to increasing a business value. It may be, but this is not the rule.

Having said responsibility team leaders had in minds responsibility for a relationship. That's right. The core of being responsible for something is responsibility for a relationship between all involved stakeholders. Doing tasks is part of it. But meeting stakeholder needs and increasing business value always comes with taking care of relationship.

About payment

I won't write anything new. But spending our own money I convinced myself that cheap is expensive if about employee's salary. We used to hire employees only by reason of their financial expectations. Mostly they were unskilled. We invested our time teaching them. When they picked up some knowledge they left the organization. So we decided to hire the best people we can afford according to our business goals.

Friday, November 15, 2013

Maturity is the Key

Do you know why: architecture, leadership, agility, effectiveness are so widely discussed? Because nobody know what they exactly are:) Or precisely: these are as complex as these mean different for different people and every point of view gives us some useful knowledge.

We coach development teams and looking in the past I may observe some interesting fenomenas. Let' see (important: I'm writing about Poland):
8 years agoNowadays
  • Some heard about XP
  • A leader aka PM was non-technical quy
  • Some heard about GoF Patterns
  • There was no terms "clean code", "implementation patterns" well known
  • Some have read "Code Complete"
  • When we talked about programmer soft skills we were understood as weirdoes
  • Teams declare using Scrum or "our own adaptive agile framework"
  • Most leaders do have a technical background
  • "patterns" is one of most discussed word in many meanings
  • Software Craftsmanship movement encourage to searching for better ways to do what we do and to improveme the craft
  • Honestly, there is so many books about "patterns", "agile", "software craftsmanship", "good enough", "code readability" I cannot handle it
  • Soft skills are widely discussed, but narrowly applied :)

So, we see the craft is changing. Software developers are changing. Nowadays, trainees or couchees are more experienced and educated then eight years ago. Some problems from the past are not problems anymore. But, we are faced with some new challenges.

What I want to say? According to Manfred Spitzer learing takes time (esspecially if we talk about social and soft skills). It is trivial, but not understood enough. We want to get skills and knowledge RIGHT NOW!. But it is impossible. Learning starts with acting based on simple procedures and lead up to contextual knowledge and contextual skills.

The crux is learning takes more time than average project length. Sometimes one expects to learn, let' say, "patterns" in four-days training. One also wants to apply its correctly in the project which is coming soon. But during a four-days training one will get a seed and an individual is responsible for its growth.

So, conclusions:
  • Software Craftsmanship is a lifelong challenge
  • See your learning goals from the five, ten or more years perspective...
  • ...but be aware that every single line of code is a small step leading you to some learning goal? Do you like this goal?
  • It is impossible to apply "patterns" or whatever always correctly
  • Yes, you will smell the code from time to time.
  • Make the failure faster :)

Tuesday, October 29, 2013

A Thought on Design Patterns

We were told a design pattern is a solution of a problem in a given context.


The metaphor above focuses our thinking on a space of a given problem. But if we extended a view a little more, we would see there is one more thing: tools.


So, a design pattern is a solution of a problem in a given context with given tools usage. And tools are obviously programming languages we use. Observing programming languages evolution (as I remember a great piece of it was given here), we may arrive at the conclustion that design patterns try to solve lacks of programming languages.

From this point of view moving to a next programming language may cause a problem disappears.


So don't try to introduce patterns to solve the problem you faced with. Find a technology where the problem is gone. This is a way I understand the Polyglot Programming movement. I think it is the right direction.

Monday, October 21, 2013

Please, define your organisational values

Dear HR Director,

I decided to write this letter, because for over five years I have worked with software engineers on their effectiveness and seen how organisational values influence software developers work.

Probably you have defined a mission statement and values in your organisation. But did you ever noticed their correlation to a quality of the source code or development processes?

Developers too often take organisational values with a grain of salt. They tell me these are just only "posters" on the walls or on their screen savers. But for me - an outer observer - it definitely is obvious that a mission statement and organisational values do their job. And do it well.

So, what I have seen? Organisations where values statements looks like:
  • We put our customers first
  • Our success is customers satisfaction
  • A customer can't wait
  • We are professionals
  • We do everything for our customers
and that is all, that organisations mostly (I said: m o s t l y) have a lot of multitasking and technical debt inside. That two lead to the even more multitasking and more smelling code as a consequences.

To be precise: all values above are good. I would be proud to identify myself with one of them. But, the problem is the values are not defined clearly enough. Engineers, managers and non-technical stuff simply do not know what to do. They try to satisfy organisational values so much, so they do just only one thing possible to be done: they guess what to do.

Of course, every individual guesses on its own account. In addition clients and deadlines bring the pressure to the teams. And here a multitasking and lack of decision making start working on software quality.

So please, define your organisational values. Define at least:
  • What kind of behaviours are acceptable
  • What kind of behaviours are not acceptable
I emphasised "behaviour" word on purpose. You have to be precise. People do not understand "be professional" but they clearly understand "do a code review". And last but not least - do what you said.


Yours sincerely,
Michal

Tuesday, September 24, 2013

The most valuable lesson learned from GTD

Whole GTD framework is worth to familiarize yourself with. But the one thing was a rocket jump for me - tasks chunking.

David Allen advice is tasks should be actionable. It means they should be ready to do now. You may examine your tasks by asking: Is it a physical action? where physical means you may express it in the language of your senses and you body. For example 'Process input data' is not physical action. Why? Try to show a process activity by your hands:). Impossible.

In opposite to tasks above: 'Talk to Peter about object model for input data', 'Draw down sketch of classes', 'Write down steps of the data processing algorithm', 'Write initial unit tests for data processing service',... are more 'physical'. So if you are aware of the context these tasks are ready to do as they are now. And this is what we call task chunking.


When I start working on a project I write down some general epics, stories, goals. But I never start working on them unless I write down at least one actionable task. In fact one actionable task is the minimum you need. Ok, I do mistakes and sometimes I take a task which is too general to accomplish it and then I always fail. The only rescue is tasks chunking.

I have also my own actionability cirterion I call it Copy-Paste Test:
'copy' a task and 'paste' it into different context; the less sense it make the more actionable it is. The reason is that specific and actionable task are strongly coupled with its context. So 'copying' them into different context make them incomprehensibled.

Consider: 'Implement the finding service' fits to every context so it is unactinable, but 'Implement service for finding Employee entity by department and salary' is stronger coupled with its context and it is more actionable.

About tools

Personally I use a kanban board on my fridge, at work we currently use LeanKit.com/. These two help me to keep clarity and limit work in progress. For tasks chunking I use todoist.com.

Thursday, June 27, 2013

[en] Code Speaks To You

A colleague asked me to translate this post for some non-Polish fellows. I've been thinking about blogging in English for couple months. Some guys from Polish blogosphere have already done so. Who knows, maybe this is sign of times. Let's this one be my first small step.


So, I had an idea for a code readability criteria: code should speaks to you. I took this sentence very seriously.

Take code piece you want to challenge and:
  1. Replace all operators by words.
  2. Replace a "{" following "if" statement by "then".
  3. Split camel case names into words.
  4. Take only right "=" operator argument. Ignore the left one.
  5. Treat each line of code as a sentence ended with a dot.
  6. And now paste the given text into Text-to-Speach engine .
  7. So if you understand (without looking at the code) what you hear, the code is readable.

Let's we try with the following code.
public class List {

 private final static int DEFAULT_SIZE = 10;
 private Object[] elements;
 private boolean readOnly;
 private int size;

 public List() {
   elements = new Object[DEFAULT_SIZE];
   size = DEFAULT_SIZE;
 }
 
 public void add(Object element) {
   if (!readOnly) {
     int newSize = size + 1;
     if (newSize > elements.length) {
       Object[] newElements = new Object[elements.length+10];
       for (int i = 0; i < size; i++) {
         newElements[i] = elements[i];
       }
       elements = newElements;
     }
     elements[size++] = element;
   }
 }
}

Challenging the add method we would have a text similar to this:
If not read only.
Size plus one.
If new size greater than elements length then.
New objects size of elements length plus one.
For each elements: i of elements.
New elements.
Element.


Now paste the text above to the Text-to-Speach engine and listen :) Does it make sense?

After small refactoring of the add method it looks little bit differently.
public void add(Object anElement) {
  if (readOnly) {
    return;
  }
  
  if (atCapacity()) {
    grow();
  }
  
  put(anElement, into(elements));
}
 


private Object[] into(Object[] elements) {
  return elements;
}

private void put(Object anElement, Object elements[]) {
  elements[size++] = anElement;
}
 
private boolean atCapacity() {
  return size + 1> elements.length;
}

private void grow() {
  Object[] newElements = new Object[elements.length + 10];
  for (int i = 0; i < size; i++) {
    newElements[i] = elements[i];
  }

  elements = newElements;
}

This time the text would look a little bit different:
If read only then return.
If at capacity then grow.
Put an element into elements.


Again, paste the text above to the Text-to-Speach engine and listen what your code speaks to you. Better? :)

Monday, June 24, 2013

Code Speaks 2U, czyli Test Ivony

Przyszło mi ostatnio do głowy następujące kryterium czytelności kodu: kod musi do Ciebie przemówić. I potraktowałem to bardzo dosłownie.

Zatem weź fragment kodu, który chcesz sprawdzić i:
  1. Operatory zamień na słowa
  2. Klamerkę "{" po ifie zamień na "then"
  3. Rozbij nazwy camel case na pojedyncze wyrazy
  4. Z wyrażeń przypisania bierz pod uwagę tylko prawą stronę, lewą ignoruj
  5. Każdy wiersz kodu traktuj jako osobne zdanie zakończone kropką.
  6. Tak powstały tekst wklej do syntezatora mowy ivona.com
  7. Jeśli to, co słyszysz (bez patrzenia na kod), jest w pełni zrozumiałe, to kod jest czytelny :)

Weźmy taki przykład na przykład:
public class List {

 private final static int DEFAULT_SIZE = 10;
 private Object[] elements;
 private boolean readOnly;
 private int size;

 public List() {
   elements = new Object[DEFAULT_SIZE];
   size = DEFAULT_SIZE;
 }
 
 public void add(Object element) {
   if (!readOnly) {
     int newSize = size + 1;
     if (newSize > elements.length) {
       Object[] newElements = new Object[elements.length+10];
       for (int i = 0; i < size; i++) {
         newElements[i] = elements[i];
       }
       elements = newElements;
     }
              elements[size++] = element;
   }
 }
}

Dla metody add tekst będzie następujący:
If not read only.
Size plus one.
If new size greater than elements length then.
New objects size of elements length plus one.
For each elements: i of elements.
New elements.
Element.

No, a teraz wklej do ivona.com i słuchaj :) Ma sens?

A teraz niewielki refaktoring metody add związany przede wszystkim nazywaniem i pierwszymi dwoma krokami Naturalnego Porządku Refaktoryzacji.

Metoda przybiera następującą postać:


public void add(Object anElement) {
  if (readOnly) {
    return;
  }
  
  if (atCapacity()) {
    grow();
  }
  
  put(anElement, into(elements));
}
 


private Object[] into(Object[] elements) {
  return elements;
}

private void put(Object anElement, Object elements[]) {
  elements[size++] = anElement;
}
 
private boolean atCapacity() {
  return size + 1> elements.length;
}

private void grow() {
  Object[] newElements = new Object[elements.length + 10];
  for (int i = 0; i < size; i++) {
    newElements[i] = elements[i];
  }

  elements = newElements;
}

Tym razem tekst dla Ivony jest następujący:
If read only then return.
If at capacity then grow.
Put an element into elements.

Ponownie odsłuchaj, co Twój kod ma Ci do powiedzenia. Lepiej? :)

Tuesday, November 22, 2011

Przeglądy kodu

Kilka słów o przeprowadzaniu code review. A zatem po co? Czy nie wystarczy podłączyć Sonara czy coś w tym stylu. Z pomocą przyszedł mi cytat zamieszczony na strone 33rd Degree Conference: A single conversation with a wise man is better than ten years of study. I właśnie dlatego warto przeprowadzać code review :) Prowadząc taką rozmowę z f2f robimy użytek efektu human infection, czyli przekazujemy sobie wiedzę i ideę w bardziej naturalny sposób niż na papierku, albo ciągiem bitów.

Informacje początkowe

Dalej streszczam mój sposób na przeprowadzanie przeglądu kodu, ale najpierw parę tematów wprowadzających.

Jak często przeprowadzać przeglądy?
Tu nic odkrywczego nie wymyślę, jeśli napiszę, że: raz na wydanie albo raz na iterację albo w regularnych odstępach czasu albo po zamknięciu zadania albo na żądanie :)

Kto przeprowadza przegląd?
Jestem fanem ról, a sceptykiem stanowisk więc sądzę, że przegląd powinien przeprowadzać lider. W żadnym razie lidero-kierownik mający pod sobą kilkanaście osób. Mówię o liderach np. konkretnych komponentów systemu, którzy przewodzą maksymalnie pięciu osobom. Jeśli brak takiego liderowato-centrycznego uporządkowania, to przegląd robią ludzie, którzy mają mandat od zespołu wskazujący, że "ten zna się na rzeczy". Rzecz jasna poszczególni programiści z biegiem czasu nabywają kompetencji do przeprowadzania przeglądów. Zespół żyje.

Jak szczegółowy ma być przegląd?
Ufam, że ludzie są mądrzy. Wystarczy pokazać im co i dlaczego robią źle, aby potem zgeneralizowali ideę i wprowadzali poprawki w reszcie kodu. Nie ma potrzeby pokazywania palcem absolutnie wszystkiego. Wiem, wiem pewnie, że są wyjątki. Ale co do zasady, wspomniane założenie ufności jest dość użyteczne.

Ile czasu zajmie przegląd?
Jeśli znasz i rozumiesz kod to w okolicach 30 min plus drugie tyle na rozmowę. Jeśli nie znasz kodu (bo na przykład analizujesz kod klienta) to już jest zupełnie inna bajka. Megaistotne jest zebranie informacji co z czym się je, żeby nie wydzwaniać do ludzi co parę minut. Sam późniejszy przegląd to 8h wzwyż.

Przegląd krok po kroku


1: Zdefiniuj w IDE tasktagi //SMELL i //REFACTOR
Będą potrzebne do oznaczania w kodzie różnych ciekawostek.

2: Ogranicz się do kodu: jednego user story | jednego zadania | jednej funkcjonalności
Generalnie mogę wpaść w cały moduł i nie wyjść z niego przez tydzień. Ale zgodnie z zasadą zaufania, chcemy wyłapać symptomatyczne niedociągnięcia delikwenta, a nie prześwietlić jego kod od A do Z. Który zatem kawałek kodu wybrać? Tu przyda się rozbudowany instynkt Dr Housa. Trzeba z premedytacją wybrać taki fragment kodu, który potencjalnie mógł programiście sprawić trudność. Przykład: ostatnim razem ustaliliście nad czym ma popracować, wiec teraz sprawdź podobne zadanie albo kątem ucha usłyszałeś jak rozmawia o jakimś trudnym zadaniu - sprawdź właśnie to. Jasne, że nie robimy tego z czystej złośliwości, lecz właśnie w tego typu zadaniach jest okazja, aby wyłapać największe niedociągnięcia i najszybciej skorygować styl pisania.

3: Wygeneruj diagram zależności między klasami/pakietami
Najczęściej korzystam z wbudowanych widoków Eclipse lub ficzerów ReSharpera, wspomagając się kartką. Teraz zaczynam zapoznawać się z CDA i jeszcze nie mam wniosków. Jeśli chodzi o C++ to Visual Assist X jest jakimś tam ułatwieniem. Ale konwencje utrzymywania projektów i plików w C++ są tak zróżnicowane, że niemal za każdym razem wymaga ode mnie kreatywnego podejścia. Zazwyczaj kończy się na Eclipse C/C++ oraz kartce i długopisie. (Nie, nie rysuję w tym przypadku diagramów w narzędziu UML, bo za długo to trwa; ewentualnie potem, gdy okazuje się na przykład, że dziesięciopoziomowa hierarchia dziedziczenia sprawia kłopoty, to coś tam przerysuję żeby efekciarsko wyglądało w raporcie).

4: Pobieżnie przyjrzyj się każdej klasie w wybranym fragmencie kodu
Przyglądamy się tu pod następującymi kątami:
  • ogólne wrażenie - elegancja kodu, czy woła o pomstę do nieba?
  • nazewnictwa - czy nazwa oddaje intencję?
  • wielkości klas i metod
  • Ilość współpracowników

5: Prześledź podstawową obsługę żądania
Chodzi o prześledzenie ścieżki od momentu, w którym ktoś zainicjował przetwarzanie

do chwili, gdy system odpowiedział na nie.

Oznacz //SMELL miejsca, w których kod wydaje Ci się niewłaściwy. Przez //REFACTOR oznacz miejsca, w których od razu wiesz, co należy zrefaktoryzować.

6: Przygotuj podsumowanie
Byle krótkie w punktach, które uwzględni relację kodu do: obowiązującego standardu kodowania, czystości kodu, przyjętych zasad architektury.

7: Przekaż informacje zwrotną
w rozmowie f2f oraz wskazuj kierunki poprawy, ewentualnie zaimprowizuj mikorszkolenie. Zanotuj sobie, aby zweryfikować, czy postanowienia z rozmowy zostały wdrożone. Jeśli ludzie będą zapominać, przeglądy nic nie wniosą i wszystkim odechce się w nie angażować.

Czy należy wnioskować, że nienawidzę narzędzi?

Absolutnie nie. Takie super gadżet jak Sonara przeanalizuje za Ciebie masę kodu i wskaże miejsca, w które warto zajrzeć osobiście. Jestem wielkim fantem użytecznych narzędzi z zastrzeżeniem, że nie wolno nam zafiksować, że narzędzia zrobią wszystko, a my będziemy mogli zwolnić się z myślenia.

Dodatkowo jestem entuzjastą minimalizowania ilości otwartych zadań (work in progress). Jeśli robię przegląd i używam fajnych okienek, wpisuję komentarze (które potem trzymane są w repo), to te komentarze tam sobie będą i będą, aż może ktoś się nimi zajmie, rośnie tendencja do odkładania. Jeśli teraz robię przegląd i mam kilka słów podsumowania na kartce, to będę dążył to tego, aby jak najszybciej złapać daną osobę, przekazać co mam do przekazania i niech ona to już zrobi. Problem zdiagnozowany, rozwiązany, można pisać dalej.

Tuesday, August 9, 2011

Interfejs budowniczego

Na przykład tutaj można dowiedzieć się o niezwykłych korzyściach stosowania wzorca Builder (na przykład do wypiekania pizzy:)). Używam zatem, ale ten budowniczy mi więcej przeszkadza niż pomaga. Można pokusić się o stwierdzenie, że to tylko dodatkowa i nad wyraz rozdmuchana warstwa pośrednia, która niczego użytecznego nie wnosi. Ot, tworzy kilka obiektów i już.

Jak wspomniałem plusy dodatnie i ujemne budowniczego były już nie raz szeroko omawiane. Mnie interesuje odpowiedź na inne pytanie: W jaki sposób zaprojektować interfejs budowniczego?. To znaczy:
  • Jak zdecydować jakie metody powinien mieć budowniczy?
  • Jak go sensownie używać (pragmatycznie, nie sztuka dla sztuki), aby pomagał, a nie przeszkadzał?


Ponieważ debugowałem blog Sławka, żeby podłączyć syntaxhighlightera, to pożyczyłem sobie Order i OrderItem do przykładu:).

Koncept jest następujący: mamy aplikację do składania zamówień na produkty. Zamówienie składa się z grup produktowych, a te składają się z produktów, jak na rysunku.

Dla uproszczenia załóżmy, że jest to aplikacja konsolowa. Właściciel oczekuje, że będzie pracował z aplikacją następująco:
Podaj identyfikator zamówienia: Paczka-E112
Podaj priorytet zamówienia: 1
Podaj nazwy grup produktowych: spożywcze chemia zabawki
Podaj ilość porduktów dla grupy 'spożywcze': 2
Dodaj produkt dla grupy 'spożywcze': sałata 2,45zł 2
Dodaj produkt dla grupy 'spożywcze': morele 6,79zł 1
Podaj ilość porduktów dla grupy 'chemia': 3
...


Startuję z następującym zestawem klas:
public class Order {
private String id;
private int priority;
private Set itemGroups = new HashSet();
}
public class ItemGroup {
private String name;
private Set items = new HashSet();
}
public class Item {
private String name;
private Money price;
private int quantity;
}


Za wykonanie zadania Dodawanie zamówienia, odpowiedzialną uczyńmy klasę:
public class UserInterface { 
private Set orders = new HashSet();
public void addOrder() {
// TOIMPL
} 
public static void main(String[] args) {
new UserInterface().addOrder();
}
}

W pierwszym podejściu kod, który działa, mógłby wyglądać na przykład tak:
public void addOrder() {
Scanner scanner = new Scanner( System.in );
System.out.print( "Podaj identyfikator zamówienia: " );
String orderId = scanner.nextLine();
System.out.print( "Podaj priorytet zamówienia: " );
int orderPriority = Integer.parseInt( scanner.nextLine() );
Order order = new Order( orderId, orderPriority );
System.out.print( "Podaj nazwy grup produktowych: " );
String[] itemGroupsNames = scanner.nextLine().split( " " );
for ( int i = 0; i < itemGroupsNames.length; ++i ) {
ItemGroup itemGroup = new ItemGroup( itemGroupsNames[ i ] );
System.out.print( "Podaj ilość produktów dla grupy '" + itemGroupsNames[ i ] + "' : " );
int itemsAmount = Integer.parseInt( scanner.nextLine() );
for( int j = 0; j < itemsAmount; ++j ) {
System.out.print( "Dodaj produkt dla grupy '" + itemGroupsNames[ i ] + "' : " );
String[] itemParams = scanner.nextLine().split( " " );
String itemName = itemParams[ 0 ];
Money itemPrice = Money.parseMoney( itemParams[ 1 ] );
int itemQuantity = Integer.parseInt( itemParams[ 2 ] ); 
Item item = new Item( itemName, itemPrice, itemQuantity );  
itemGroup.addItem( item );
}
order.addItemGrup( itemGroup );
}
orders.add( order ); 
}
Po pobieżny przyjrzeniu się temu rozwiązaniu, można zauważyć, że nastąpiło tu pewne pogmatwanie. Komunikowanie się z użytkownikiem (wyświetlanie komunikatów i odbieranie wpisanych danych) zostało zmiksowane z kodem biznesowym (budowanie zamówienia). W pierwszym zachwycie nad wzorce Builder mam ogromną ochotę go użyć! UWAGA jeśli przychodzi ci go głowy napisać klasę OrderBuilder z jedną metodą build mającą kilkanaście parametrów, to nie tędy droga. Wyszedł by z tego nie budowniczy ale jakieś fabrykoniewiadomoco. Wracając do tematu: wprowadźmy zatem ad hoc budowniczego (bo ponoć to dobra praktyka). Budowniczy ten mógłby wyglądać następująco:
public class OrderBuilder {
private Order order;
public void newOrder( String orderId, int orderPriority ) {
order = new Order( orderId, orderPriority );
}
public Order getOrder() {
return order;
}
public void addItemGroup( String name ) {
order.addItemGrup( new ItemGroup( name ) );
}
public void addItem( String groupName, String name, Money price, int qty ) {
ItemGroup group = order.findItemGroup( name );
group.addItem( new Item( name, price, qty ) );
}
}

Kluczowe pytanie: Czy ten budowniczy jest pomocny?

Już zerkając na kod budowniczego, można mieć wątpliwości. Właściwie nie robi nic ponad, enkaspulację wywołań konstruktorów oraz metod dodających. Ok, można go sobie mokować i dodawać nowe implementacje, ale zobaczmy jak wygląda kod, który użytkuje tego budowniczego:
public void addOrder() {
Scanner scanner = new Scanner( System.in );
System.out.print( "Podaj identyfikator zamówienia: " );
String orderId = scanner.nextLine();
System.out.print( "Podaj priorytet zamówienia: " );
int orderPriority = Integer.parseInt( scanner.nextLine() );
OrderBuilder builder = new OrderBuilder();
builder.newOrder( orderId, orderPriority );
System.out.print( "Podaj nazwy grup produktowych: " );
String[] itemGroupsNames = scanner.nextLine().split( " " );
for ( int i = 0; i < itemGroupsNames.length; ++i ) {
builder.addItemGroup( itemGroupsNames[ i ] );
System.out.print( "Podaj ilość produktów dla grupy '" + itemGroupsNames[ i ] + "' : " );
int itemsAmount = Integer.parseInt( scanner.nextLine() );
for( int j = 0; j < itemsAmount; ++j ) {
System.out.print( "Dodaj produkt dla grupy '" + itemGroupsNames[ i ] + "' : " );
String[] itemParams = scanner.nextLine().split( " " );
String itemName = itemParams[ 0 ];
Money itemPrice = Money.parseMoney( itemParams[ 1 ] );
int itemQuantity = Integer.parseInt( itemParams[ 2 ] ); 
builder.addItem( itemGroupsNames[ i ], itemName, itemPrice, itemQuantity );
}
}
orders.add( builder.getOrder() );
}
Jest tak samo brzydki jak był! Budowniczy nie wniósł znaczącego wkładu do tego kodu. Moim zdaniem interfejs budowniczego należy projektować z perspektywy klienta. Pisząc klient mam na myśli usługę, która akurat budowniczego będzie wykorzystywać - w tym przypadku jest to klasa UserIterface. Interfejs budowniczego powinien być wygodny dla klienta.

Krok 1: Dlaczego ItemGroup i Item trzeba dodawać pojedynczo?

Wcale nie trzeba...Przecież budowniczy może mieć metody, które przyjmą input, który przyszedł od użytkownika i samodzielnie go zinterpretuje. Pytanie: Czy czasem znów nie dojdzie do pomieszania warstw? Nie nie dojdzie. Przeanalizujmy odpowiedzialności poszczególnych klas:
  • Order, ItemGroup, Item - to model dziedziny, reprezentuje rzeczywistość
  • UserInterface - komunikuje się z użytkownikiem; odbiera od niego żądania (wpisy z konsoli) i przekazuje je do odpowiednich elementów niżej; prezentuje użytkownikowi wynik działania systemu
  • OrderBuilder - służy do złożenia zamówienia z mniejszych elementów
Zatem jeśli budowniczy może spokojnie przyjąć stringi podane przez użytkownika, a potem je zinterpretować. Dopóki budowniczy nie zacznie pisać na konsolę, to odpowiedzialność klas zostanie zachowana. Zobaczmy jak to mogłoby wyglądać:
public class OrderBuilder {
private Order order;
public void newOrder( String orderId, int orderPriority ) {
order = new Order( orderId, orderPriority );
}
public void addItemGroups( String itemsGroupsInput ) {
String[] itemGroupsNames = itemsGroupsInput.split( " " ); 
for ( int i = 0; i < itemGroupsNames.length; ++i ) {   
order.addItemGrup( new ItemGroup( itemGroupsNames[ i ] ) );
}
}
public void addItem( String itemParamsInput ) {
String[] itemParams = itemParamsInput.split( " " );
String groupName = itemParams[ 0 ];
ItemGroup group = order.findItemGroup( groupName );
Money price = Money.parseMoney( itemParams[ 1 ] );
int qty = Integer.parseInt( itemParams[ 2 ] );   
group.addItem( new Item( groupName, price, qty ) );
}
public Order getOrder() {
return order;
}
}
Jak widać budowniczy zmądrzał nieco, potrafi zrobić coś więcej niż proste dodawanie. A jak wygląda korzystanie z niego?
public void addOrder() {
Scanner scanner = new Scanner( System.in ); 
System.out.print( "Podaj identyfikator zamówienia: " );
String orderId = scanner.nextLine();
System.out.print( "Podaj priorytet zamówienia: " );
int orderPriority = Integer.parseInt( scanner.nextLine() );
OrderBuilder builder = new OrderBuilder();
builder.newOrder( orderId, orderPriority );
System.out.print( "Podaj nazwy grup produktowych: " );
String itemsGroupsInput = scanner.nextLine();
builder.addItemGroups( itemsGroupsInput );
String[] itemGroupsNames = itemsGroupsInput.split( " " );
for ( int i = 0; i < itemGroupsNames.length; ++i ) {
System.out.print( "Podaj ilość produktów dla grupy '" + itemGroupsNames[ i ] + "' : " );
int itemsAmount = Integer.parseInt( scanner.nextLine() );
for( int j = 0; j < itemsAmount; ++j ) {
System.out.print( "Dodaj produkt dla grupy '" + itemGroupsNames[ i ] + "' : " );
String itemParamsInput = scanner.nextLine();
builder.addItem( itemParamsInput );
}
}
orders.add( builder.getOrder() );
}
Choć nie idealnie to jednak jest nieco lepiej - metoda mieści się na jednym ekranie:)

Krok 2: Chodzenie po strukturze zamówienia

To, co nam bruździ to fakt, trze trzeba dodać Items do każdej z ItemGroup. Więc najpierw za pomocą OrderBuilder.addItemGroups tworzone są wszystkie grupy, ale jeszcze dodatkowo UserInterface przetrzymuje sobie jeszcze tablicę nazw grup produktowych, że by po nich przeiterować i dodać pozycje zamówienia. Wydaje się to trochę nienaturalne, ponieważ ta sama informacja jest przechowywana w dwóch różnych formach (String[] i Set) w dwóch różnych miejscach (UserInterface i OrderBuilder). Skoro chcemy tylko chodzić po wewnętrznej strukturze zamówienia (przesuwać się do kolejnych grup i dodawać produkty), to dlaczego nie wyposażyć budowniczego w tę możliwość. Niech budowniczy udostępni zestaw metod do chodzenia po grupach produktowych. Zobaczmy:
public class OrderBuilder {
private Order order;
private Iterator groupIterator; 
private ItemGroup currentGroup;
public void newOrder( String orderId, int orderPriority ) {
order = new Order( orderId, orderPriority );
}
public void addItemGroups( String itemsGroupsInput ) {
String[] itemGroupsNames = itemsGroupsInput.split( " " );  
for ( int i = 0; i < itemGroupsNames.length; ++i ) {   
order.addItemGrup( new ItemGroup( itemGroupsNames[ i ] ) );
}
groupIterator = order.getItemGroups();
}
public void addItem( String itemParamsInput ) {
String[] itemParams = itemParamsInput.split( " " );
String groupName = itemParams[ 0 ];
Money price = Money.parseMoney( itemParams[ 1 ] );
int qty = Integer.parseInt( itemParams[ 2 ] );   
currentGroup.addItem( new Item( groupName, price, qty ) );
}
public Order getOrder() {
return order;
} 
public boolean hasNextItemGroup() {
if ( groupIterator == null ) {
return false;
}  
return groupIterator.hasNext();
}
public void moveNextItemGroup() {
currentGroup = groupIterator.next();
} 
public String getCurrentItemGroupName() {
return currentGroup.getName();
} 
}
Natomiast korzystanie z budowniczego wygląda następująco:
public void addOrder() {
Scanner scanner = new Scanner( System.in ); 
System.out.print( "Podaj identyfikator zamówienia: " );
String orderId = scanner.nextLine();
System.out.print( "Podaj priorytet zamówienia: " );
int orderPriority = Integer.parseInt( scanner.nextLine() );
OrderBuilder builder = new OrderBuilder();
builder.newOrder( orderId, orderPriority );
System.out.print( "Podaj nazwy grup produktowych: " );
builder.addItemGroups( scanner.nextLine() );
while ( builder.hasNextItemGroup() ) {
builder.moveNextItemGroup();
System.out.print( "Podaj ilość produktów dla grupy '" +       builder.getCurrentItemGroupName() + "' : ");
int itemsAmount = Integer.parseInt( scanner.nextLine() );
for( int j = 0; j < itemsAmount; ++j ) {
System.out.print( "Dodaj produkt dla grupy '" + builder.getCurrentItemGroupName() + "' : " );
builder.addItem( scanner.nextLine() );
}
}
orders.add( builder.getOrder() );
}
Teraz już klasa UserInterface zajmuje się wyłącznie komunikacją z użytkownikiem, natomiast budowniczy zajmuje się składaniem zamówienia do kupy. Każda z klas zachowała swoją odpowiedzialność, a z używanie budowniczego rzeczywiście przynosi korzyści klientowi większe niż tylko enkapsulacja konstruktorów i dodawania do list. Dzięki wykorzystaniu budowniczego kod kliencki staje się prostszy i bardziej przejrzysty. Dla porządku trzeba dodać, że odpowiedzialność budowniczego została zwiększona. Potrafi on teraz również chodzić po strukturze zamówienia. Jest więc jednocześnie Iteratorem. Dalej warto metody iterator wydzielić do osobnego interfejsu, ale to już nieco inna historia.

Wednesday, July 6, 2011

Poza wzorcami projektowymi

To jest odległa kontynuacja artykułu Język wzorców.

Nie pamiętam już u kogo, ale wyczytałem, że jedną z przyczyn powstania wzorców projektowych, było rozwiązanie różnego rodzaju bolączek technologicznych albo bolączek konkretnych języków programowania. Zacząłem się nad tym zastanawiać. I tak:
  • Null Object - bo w C++ referencja musi być zainicjowana; wyeliminowane przez Java, C# (nieścisłość, @see Komentarz pod postem Chrisphera Daniela)
  • State - bo implementacja maszyny stanów oparta o tablicę wskaźników do funkcji oraz serię switch/case
  • jest dość trudna w utrzymaniu
  • Interpreter - jaki tam wzorzec;)? sposób na parsowanie ustrukturyzowanych gramatyk i tyle; wyeliminowane przez tony bibliotek narzędziowych
  • Flyweigh - bo nie ma kasy na więcej pamięci
  • Command - w C# wykoszony przez delegaty (mam na myśli ten kontekst użycia, w którym robimy execute(), a nie przechowujemy komendę, aby ją potem handle() )
  • Właściwie wszystkie wzorce opisane w Core J2EE Patterns i PoEAA - zastąpione przez biblioteki, frameworki i technologie


Czym zatem są wzorce projektowe? Mówi się, że rozwiązaniami typowych problemów. Rozwiązaniami, które wg Alexandra, można stosować za każdym razem nieco inaczej (nieco inna implementacja), ale otrzyma się poprawne rozwiązanie konkretnego problemu. W porządku - może postęp technologiczny modyfikuje tylko implementacje wzorców, lecz idea zostaje podobna. Może jednak nie - czy w wzorzec zamknięty w wszytywne API biblioteki wciąż jest wzorcem, czy tylko zwyczajnym narzędziem pomocniczym?

Jak powstają?
Skąd się biorą i jak powstają? Istnieją trzy elementarne zasady postępowania, które prędzej czy później doprowadzą do dobrego rozwiązania obiektowego:
Przestrzegaj opodwiedzialności
Jeśli nie potrafimy czegoś nazwać, to prawdopodobnie nie rozumiemy jak to działa. Nie znamy odpowiedzialności tego czegoś. W swoim kodzie dbaj o to, aby każda: zmienna, metoda, klasa, interfejs, pakiet, moduł miały określoną tylko jedną opodwiedzialność, która będzie wyrażona nazwą
Enkapsuluj
To, co podlega zmianom, powinno zostać opakowane (w metodę lub w klasę - w zależności od potrzeby). Enkapsulacja pozwala na łatwiejsze zarządzanie zmianą w kodzie. Nie mówiac już o testowaniu.
Preferuj kompozycję ponad dziedziczenie
Związki używania czynią kod bardziej elastycznym, czytelnym i testowalnym. Dodatkowo pozwalają na rozszerzanie funkcjonalności kodu run-time, a nie tylko poprzez przekompilowanie.


Jeśli przestrzegamy tych zasad, prawie na pewno powstanie dobry kod. Jest jeden warunek: trzeba stosować je bardzo skrupulatnie. Żadnego wkuwania nazw wzorców, diagramów, definicji, itd. Zamiast tego: żelazna dyscyplina w stosowaniu w/w zasad.

Wymienione reguły są dość niskopoziomowe, w całościowy proces ujmuje koncept, który nazwaliśmy roboczo Nautralnym porządkiem refaktoryzacji.
Koncept Nautralnego porządku refaktoryzacji


Pisał już o tym sporo Mariusz w artykułach: NPR - Idea rysunkowa, NPR - pod lupą, NPR: Compose Method, NPR: Extract Method, NPR: Refaktoryzacja do wzorców, NPR: Ewolucja architektury, więc nie będę się powtarzał.

Powtarzając konsekwentnie ten proces i stosując wspomniane już trzy metody, otrzymujemy naprawdę porządny kod. Zaczynają pojawiać się dobre rozwiązania. Dość często są to znane wzorce projektowe.

Morfizmy w kodzie
Zauważmy, że w charakterystyka wzorca projektowego zawiera między innymi Motywację/Intencję, Kontekst, Problem. Według definicji wzorzec to rozwiązanie problemu w danym kontekście. Ale przecież nieustanie zmienia się zarówno kontekst, jak i nasze zrozumienie problemu. Wszystko się zmienia!

Aktualne dobre rozwiązanie, które przywykliśmy nazywać wzorcem projektowym, jest tak naprawdę lokalnym optimum, jakieś funkcji (powiedzmy, że Funkcji kodu) w której zmiennymi są przynajmniej: Kontekst oraz Problem.

Tak zwany wzorzec rozwiązuje problem tylko na chwilę, dopóki Funkcja kodu nie ulegnie zmianie, a optimum nie wystrzeli w inną stronę. Mamy wiec do czynienia nie tyle z wzorcami, co z optymalnymi morfizmami w kodzie (niech, Ockham mi wybaczy). Zmiana Funkcji kodu, po której programując się ślizgamy powoduje, że dany morfizm przestaje być optymalny i musimy poszukać nowego optimum właśnie poprzez refaktoryzację.
Optymalne morfizmy


Wnioski
Czy poza gimnastyką umysłu wypływają stąd jakieś wnioski? Owszem, kilka.
  1. Istnieje nieskończenie wiele optymalnych morfizmów w kodzie - ponieważ zakres zmienności Problemów oraz Kontekstów jest również nieskończony; w związku z tym publikacje i dywagacje na temat nowych wzorców projektowych nigdy się nie skończą
  2. Refaktoryzacja musi trwać - ponieważ Funkcja kodu wciąż się przekształca, wciąż musimy poszukiwać optymalnych morfizmów; zaprzestanie tych działań spowoduje takie zniekształcenie tej funkcji, że znalezienie optymalnych morfizmów będzie niemożliwe i trzeba będzie ją wyrysować na nowo - czytaj napisać system raz jeszcze
  3. Ważniejsze jest zdyscyplinowane posługiwanie się wspomnianymi wyżej zasadami i procesem refaktoryzacji niż szastanie kolekcjami nazw wzorców
  4. Nie jest możliwe stworzenie czegoś takiego jak patterns language (por: On Patterns and Pattern Languages), bo optymalne morfizmy wciąż się przekształcają podążając za zmianami Funkcji Kodu

Saturday, October 2, 2010

30 lat z życia programisty

Miałem ostatnio przyjemność pracować z programistami, które swoje pierwsze programy tworzyli korzystając z kart perforowanych lub kodu maszynowego procesora. Tak istnieją jeszcze tacy...

Zastanawiałem się w jaki sposób Ci ludzie utrzymali się w tej branży tak długo, gdzie przecież co chwila jesteśmy świadkami skoków technologicznych, gdzie wciąż trzeba poszerzać swoje umiejętności albo przekwalifikowywać się. Pewnie pomyślicie, że Ci ludzie zachomikowali się gdzieś na wygodnych stanowiskach i dłubią swoje w COBOLu (nawiasem mówiąc stawka programisty COBOLa to $5000/m-c). Otóż nie, jest zupełnie inaczej.

Najczęściej spotykam programistów w wieku do 30 lat z doświadczeniem od 0 do 8 lat. Większość z nich jest absolutnie przekonana, że OOP rulez! Bardzo wielu nie widzi świata poza Javą, .NETem albo C/C++ - w zależności od tego co piszą. Są absolutnymi fanami tego co robią. I dobrze!
Czym zatem różnią się od programistów z 30 letnim stażem? Przede wszystkim tym, że Ci drudzy są absolutnymi fanami pragmatyzmu.Wciąż tworzą rozwiązania, które zaspokajają potrzeby klientów. Dla wielu z nich OOP albo Java to kolejna nowinka technologiczna. Z mojej perspektywy mają oni niemal ponadczasowe spojrzenie na branżę IT.

Gdy tak roztrząsaliśmy wspólnie różne rozwiązania w systemach IT i próbowałem przekonywać ich do moich ulubionych wizji i ulubionych technologii, jeden z nich uśmiechnął się i powiedział wiesz, z tego co obserwuję co 8-10 lat następuje jakiś gwałtowny zwrot w technologii, jakaś nowinka, która zupełnie odmienia sposób w jaki programiści myślą i tworzą; przeszedłem już przez kilka takich zmian, teraz kończy się mniej więcej 8 lat od dżawowego szału i jestem bardzo ciekaw co będzie dalej...

Moi rozmówcy wieszczyli, że zbliża się era języków funkcyjnych, no zobaczymy czy mają rację.

Życzę sobie, abym kiedyś również potrafił patrzyć na swoją pracę i pracę innych z takim dystansem i trzeźwością co programiści z trzydziestoletnim stażem.