Nested function in Clojure with defn and letfn

After reading spoon-bending and delightfull beginning of SICP I tried to write some Clojure.

Resources witch I found usefull enough to check them out before google my question:

Here is what I came up when trying to write the examples in Clojure:

(def square #(* %1 %1))
(def avg #(/ (+ %1 %2) 2))
(def abs? #(if (< %1 0) (- %1) %1))

(defn improve [so-far x]
  (avg so-far
       (/ x so-far)))

(defn good-enough? [so-far x]
  (< (abs? (- (square so-far) x))
     0.00001))

(defn sqrt-newton [so-far x]
  (if (good-enough? so-far x)
      so-far
      (sqrt-newton (improve so-far x)
                   x)))

(def sqrt #(sqrt-newton (/ %1 2) %1))

(defn sqrt [x] (sqrt-newton (/ x 2) x))

and after a while trying to use nested functions:

(def square #(* %1 %1))
(def avg #(/ (+ %1 %2) 2))
(def abs? #(if (< %1 0) (- %1) %1))

(defn sqrt [x]
   (letfn [
    (improve [so-far] (avg so-far (/ x so-far)))
    (good-enough? [so-far] (< (abs? (- x (square so-far)))
                              0.00001))
    (sqrt-newton [so-far] (if (good-enough? so-far)
                              so-far
                              (sqrt-newton (improve so-far))))]
    (sqrt-newton (/ x 2))))

Is it proper way to define local functions. Constructive criticism is very welcome.

Advertisements
Posted in Uncategorized | Tagged , | Leave a comment

Implementacja hashCode i equals dla encji

Jak poprawnie zaimplementować hashCode() i equals() dla encji? Dokumentacja do Hibernate, daje prostą wskazówkę: użyj klucza biznesowego o ile taki istnieje. Co jednak jeśli takiego klucza nie ma? Czy istnieje dobra dobre rozwiązanie lub chociaż dostatecznie dobre 🙂

By sprawdzić jakie problemy mogą wystąpić z różnymi implementacjami, zbuduję proste narzędzie weryfikujące(source code). Metody equals() i hashCode() będę testował dla klasy zawierającej pole odpowiadające PK oraz inna właściwość:

public class SomeClass {

  private Integer id;
  private String name;

  public SomeClass() {
    this(null, null);
  }

  public SomeClass(Integer id, String name) {
    this.id = id;
    this.name = name;
  }

  // ... dla uproszczenia dalej geterry i settery
}

Oto testy sprawdzające podstawowe warunki nakładane na equals() przez Javadoc:

	@Test
	public void equalsIsReflective() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = new SomeClass(null, "other");

		// then
		assertEqualsIsRefelective(one);
		assertEqualsIsRefelective(three);
	}

	private void assertEqualsIsRefelective(Object one) {
		assertTrue(one.equals(one));
	}

	@Test
	public void equalsIsSymetric() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = new SomeClass(null, "other");

		// then
		assertEqualsIsSymetric(one, three);
	}

	private void assertEqualsIsSymetric(Object one, Object three) {
		boolean isEquals = one.equals(three);
		assertEquals(three.equals(one), isEquals);
	}

	@Test
	public void equalsIsTransitive() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass two = new SomeClass(1, "two");
		SomeClass three = new SomeClass(1, "other");

		// then
		assertEqualsIsTransitive(one, two, three);
	}

	private void assertEqualsIsTransitive(Object one, Object two, Object three) {
		if(one.equals(two) && two.equals(three)) assertTrue(one.equals(three));
	}

	@Test
	public void equalsIsFalseForNull() {
		SomeClass one = new SomeClass(1, "one");
		assertEqualsIsFalseForNulls(one);
	}

	private void assertEqualsIsFalseForNulls(Object one) {
		assertFalse(one.equals(null));
	}

Podobnie dla metody hashCode():

	@Test
	public void hashCodeForeEqualObjectIsEqual() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass two = new SomeClass(1, "two");

		// then
		assertHashCodeTheSameWhenEquals(one, two);
	}

	private void assertHashCodeTheSameWhenEquals(Object one, Object two) {
		if (one.equals(two))
			assertTrue(one.hashCode() == two.hashCode());
	}

	@Test
	public void hashCodeDontChangeWhenNotEqualDependentFieldChanges() {
		// given
		SomeClass one = new SomeClass(1, "one");
		int hashCode = one.hashCode();

		// when
		one.setName("other");

		// then
		assertEquals(one.hashCode(), hashCode);
	}

Oto kilka dodatkowych warunków, pozwalających na efektywne korzystanie ze standardowych kolekcji Java:

	@Test
	public void shouldFindInHashSet_IdNullAndNotNull() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = new SomeClass(null, "three");

		// when
		Set set = new HashSet();
		set.add(one);
		set.add(three);

		// then
		assertTrue(set.contains(one));
		assertTrue(set.contains(three));
		assertEquals(set.size(), 2);
	}

	@Test
	public void shouldFindInHashMap_IdNullAndNotNull() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = new SomeClass(null, "three");

		// when
		Map set = new HashMap();
		set.put(one, one.getName());
		set.put(three, three.getName());

		// then
		assertTrue(set.containsKey(one));
		assertTrue(set.containsKey(three));
		assertEquals(set.size(), 2);
	}

	@Test
	public void shouldDistinguish_IdNullAndNotNull() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = new SomeClass(null, "three");

		assertNotEquals(three, one);
	}

	@Test
	public void shouldNotDistinguish_SameId() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass other = new SomeClass(1, "other");

		// then
		assertTrue(one.equals(other));
		assertTrue(other.equals(one));
	}

	@Test
	public void shoulDistinguish_twoNullId() {
		// given
		SomeClass one = new SomeClass(null, "one");
		SomeClass two = new SomeClass(null, "two");

		// then
		assertFalse(one.equals(two));
	}

	@Test
	public void shouldFindInHashSet_twoNullId() {
		// given
		SomeClass one = new SomeClass(null, "one");
		SomeClass three = new SomeClass(null, "three");

		// when
		Set set = new HashSet();
		set.add(one);
		set.add(three);

		// then
		assertTrue(set.contains(one));
		assertTrue(set.contains(three));
		assertEquals(set.size(), 2);
	}

	@Test
	public void shouldFindInHashMap_twoNullId() {
		// given
		SomeClass one = new SomeClass(null, "one");
		SomeClass three = new SomeClass(null, "three");

		// when
		Map set = new HashMap();
		set.put(one, one.getName());
		set.put(three, three.getName());

		// then
		assertTrue(set.containsKey(one));
		assertTrue(set.containsKey(three));
		assertEquals(set.size(), 2);
	}

Poniższe testy pozwolą na wykrycie zachowań obiektów przy zmianie ich wewnętrznego stanu:

	@Test
	public void shouldFindInHashSet_afterIdChanged() {
		// given
		SomeClass one = new SomeClass(null, "one");

		// when
		Set set = new HashSet();
		set.add(one);

		one.setId(42); // e.g. persist this object

		// then
		assertTrue(set.contains(one));
		assertEquals(set.size(), 1);
	}

	@Test
	public void shouldFindInHashMap_afterIdChanged() {
		// given
		SomeClass one = new SomeClass(null, "one");

		// when
		Map set = new HashMap();
		set.put(one, one.getName());

		one.setId(42); // e.g. persist this object

		// then
		assertTrue(set.containsKey(one));
		assertEquals(set.size(), 1);
	}

	@Test
	public void shouldFindInHashSet_afterNameChanged() {
		// given
		SomeClass one = new SomeClass(null, "one");

		// when
		Set set = new HashSet();
		set.add(one);

		one.setName("other"); // some business logic operation

		// then
		assertTrue(set.contains(one));
		assertEquals(set.size(), 1);
	}

	@Test
	public void shouldFindInHashMap_afterNameChanged() {
		// given
		SomeClass one = new SomeClass(null, "one");

		// when
		Map set = new HashMap();
		set.put(one, one.getName());

		one.setName("other"); // some business logic operation

		// then
		assertTrue(set.containsKey(one));
		assertEquals(set.size(), 1);
	}

Brak implementacji

Mając taki zestaw testów, zobaczmy jaka zachowa się klasa SomeClass, gdy pozostawimy domyślną implementacją hashCode() i equals() z Object().

Jedyny problem jaki został wykryty to brak rozpoznawania dwóch obiektów o takich samych identyfikatorach bazodanowych jako takich samych. Czy to duży problem? Jeśli chcemy tworzyć algorytmy przetwarzające obiekty pobrane z różnych sesji, lub też wykorzystywać jako kolekcje zbiory to tak to bardzo duży problem. Ostrzega przed tym dokumentacja Hibernate.

Implementacja generowana przez Eclipse operująca na PK

Zobaczmy jak zachowa się wyegenerowanie hashCode() i equals() z Eclipse:

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SomeClass other = (SomeClass) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

Uruchomienie testów pokazuje dwa poważne problemy:

  • obiektu nie da się pobrać z HashSet czy użyć jako klucza w HashMap
  •  dwa obiekty o identyfikatorze null są traktowane jak takie same
  • po zmianie identyfikatora nie da się obiektu pobrać z HashSet lub HashMap

Lekka modyfikacja metody equals() rozwiązuje dwa pierwsze problemy:


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SomeClass other = (SomeClass) obj;
		if (id == null) {
			// if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

W ten sposób dochodzimy do dwóch rozwiązań, z których każde wiąże się z ograniczeniami. Tyle jeśli chodzi o podstawy. Drugie rozwiązane oparte o equals() i hashCode() bazujące na kluczu podstawowym można dodatkowo poprawić.

Dynamiczne proxy

Co się stanie gdy framework ORM będzie posługiwał się proxy naszego obiektu a nie istancją naszej klasy? Aby zrozumiec problem, wykorzystamy Javasist do utworzenia proxy dla instancji SomeClass, które będzie po prostu delegowało wywołania do klasy na podstawie którego zostało utworzone:


	public static SomeClass buildSomeClass(final SomeClass baseObject) {
		ProxyFactory f = new ProxyFactory();
		f.setSuperclass(SomeClass.class);
		f.setFilter(new MethodFilter() {
			public boolean isHandled(Method m) {
				return !m.getName().equals("finalize");
			}
		});
		@SuppressWarnings("rawtypes")
		Class c = f.createClass();
		MethodHandler mi = new MethodHandler() {
			public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Throwable {
				return m.invoke(baseObject, args);
			}
		};
		SomeClass foo = null;
		try {
			foo = (SomeClass) c.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
			return null;
		}
		((ProxyObject) foo).setHandler(mi);
		return foo;
	}

Dodajemy dodatkowe sprawdzenia:

@Test
	public void shouldRecognizeEqualsWhenSameId() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = SimpleDelegatingProxyMaker.buildSomeClass(new SomeClass(1, "three"));

		// then
		assertTrue(one.equals(three));
		assertTrue(three.equals(one));
	}

	@Test
	public void shouldRecognizeDifferWhenDifferentId() {
		// given
		SomeClass one = new SomeClass(1, "one");
		SomeClass three = SimpleDelegatingProxyMaker.buildSomeClass(new SomeClass(2, "three"));

		// then
		assertFalse(one.equals(three));
		assertFalse(three.equals(one));
	}

	@Test
	public void shouldRecognizeSameWhenBothNullId() {
		// given
		SomeClass one = new SomeClass(null, "one");
		SomeClass three = SimpleDelegatingProxyMaker.buildSomeClass(new SomeClass(null, "three"));

		// then
		assertFalse(one.equals(three));
		assertFalse(three.equals(one));
	}

Oba poprzednie rozwiązania są obarczone tym samym problemem:, metoda equals() sprawdza czy klasy obu obiektów są identyczne, dlatego metoda equals() zwraca false. Dodatkowy problem to odwoływanie się do pól w metodzie equals() a nie do metod, co szerzej opisane jest na blogu Xebia.

Rozwiązanie uwzględniające oba problemy mogło by wyglądać tak:

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (! (obj instanceof SomeClass) )
			return false;
		SomeClass other = (SomeClass) obj;
		if (getId() == null) {
//			if (other.id != null)
				return false;
		} else if (!id.equals(other.getId()))
			return false;
		return true;
	}

Trzeba być jednak świadomym, iż tracimy w ten sposób zabezpieczenie przed porównywaniem obiektów typu bazowego i typu go rozszerzającego o identycznych identyfikatorach.

Nieefektywny hashCode()

Problemy z odnajdywaniem obiektu w kolekcjach po zmianie id są spowodowane zmianą wartości hashcode. Jeśli dodamy obiekt do kolekcji wykorzystującej haszowanie, trafi ona do pódełka na podstawie wartości zwracanej przez hashCode(). Jeśli zmieni się id obiektu np w wyniku jego zapisu w bazie danych i przyznajiu mu automatycznego id, obiekt będzie miał inny hashCode(), a zatem metody próbujące go pobrać będą szukały go w niewaściwym pudełku.

Słyszałem o 2 rozwiązaniach. Pierwsze polega na wykorzstaniu jednej z najbardziej nieefektywnych i jednocześnie najprostszych metod implementacji hashCode():

	@Override
	public int hashCode() {
		return 1;
	}

Proste 🙂

Rozwiązanie oparte o sztuczne id biznesowe

Kolejne rozwiązanie pojawia się w różnych wariantach w dyskusji na forum Hibernate:


//...

private volatile Object identyfiObject;

//...

	@Override
    public boolean equals(Object obj) {
        final boolean result;
        if (obj instanceof SomeClass) {
            return getObject().equals(((SomeClass)obj).getObject());
        } else {
            result = false;
        }
        return result;
    }

	@Override
    public int hashCode() {
        return getObject().hashCode();
    }
	private Object getObject() {
        if (getIdentyfiObject() != null || getIdentyfiObject() == null && getId() == null) {
            if (getIdentyfiObject() == null) {
                synchronized(this) {
                    if (getIdentyfiObject() == null)
                    	setIdentyfiObject(new Object());
                }
            }
            return getIdentyfiObject();
        }
        return getId();
    }
	private Object getIdentyfiObject() {
		return identyfiObject;
	}
	private void setIdentyfiObject(Object identyfiObject) {
		this.identyfiObject = identyfiObject;
	}

Pomijając dostęp za pomocą metod a nie odwołania do pól, sprowadza się ona do utworzeniu dodatkowego pola, w którym przechowywane jest identyfikator. Jest ono równe PK jeśli wcześniej nikt nie użył metody hashCode() lub equals(). Jeśli jednak ktoś ich użyje, jest tworzony nowy obiekt i do niego delegowane są odwołania do hashCode() i equals().

Rozwiązanie to nie jest zbyt przejrzyste, spotkało się z bużliwą dyskusją na wspomnianym uprzednio forum.

Myślę, iż warto samemu przejść wszystkie te przypadki by zrozumieć jakie są problemy z różnymi podejściami. Warto też poeksperymentować z błędnymi w oczywisty sposób pomysłami jak equals() bez hashCode().

Ciekawe też, mogą być długoterminowe skutki zastosowania niektórych implementacji 🙂

Posted in Uncategorized | Tagged , | 2 Comments

Functional, message passing, parallel patterns in Erlang

Is there exists any? Isn’t functional languages expressive enough to don’t bother with patterns? I won’t give another rant about that topic. Interested people i encourage to look very fun and insightful presentation Functional Design Patterns by Aino Corry on InfoQ.

There are many very useful concepts, ideas techniques (choose your best matching word 🙂 ) in functional programming. I am not an expert, and I don’t know many functional languages (i have played a little with Scheme and used commercially Erlang for a few months) – so i may change my mind about it. But currently I think that there are general enough, sometimes not obvious enough and reoccurring patterns in functional style of programming.

Functional patterns

Functional programming is all about defectively using higher order functions, ignoring concept of shared state, doing cool things with lists and using recursion (always tail of course). So this determine basic areas of patterns:

The concept of using closures to share state in a few dynamically created functions Ihave  seen in famous Structure and Interpretation of Computer Programs. I suspect that studying this book alone (and lisp) might bring me a few powerful concepts.

The concepts of fancy list manipulation (lists:foldl, lists:map) is probably basic enough to not be called patterns. Similar situation with pattern matching using to bound variables and extracting elements from data structures. List comprehension is the syntactic construct so we can skip it probably .

Message passing patterns

In Erlang community patterns means OTP behaviours:

  • gen_server (remembering some small portion of state by passing it to internally in every invocation, synchronous and asynchronous calls)
  • supervisor (managing e.g. restarting after failure work of other tasks)
  • gen_fsm (Finite State Machine pattern)
  • gen_event (sending events)

There is a few concepts described in Programming Erlang by Joe Armstrong which AFAIK are not directly supported by those behaviours: priority receiving, flushing. But again are probably basic enough to not count as patterns.

Parallel computation patterns

Took me while to find out some tips on how to use Erlang message passing capabilities to do parallel computing. OK I know that I should bang messages to other process mailbox, i could register them or use pids but how actually use them?

Mentioned above book describe this in chapter 20. I found few interesting functions in OTP to support that: rpc:pmap and rpc:parallel_eval. They are very powerful hiding a lot of details about synchronizing results and spreading computation for different nodes/processes. In rpc module there are many interesting functions. And what is most important basic pmap concept can be modified to achieve different building block to create parallel algorithm (see the 20 chapter for details). In here i can’t name patterns yet but i hope this will change.

Other

I don’t know Haskell concepts of monads and lazy evaluation so can’t say anything about them in context of Erlang or functional languages in general. Maybe there are a lot of powerful concepts hiding there 🙂

Posted in Uncategorized | Tagged , , | Leave a comment

How to use edoc?

Generating Erlang documentation by edoc

For entire application (use atom for your application name to avoid problems):

edoc:application(myapp, ["."], [{dir, "doc"}]).

For single file:

edoc:run([], ["src/mymodule.erl"], [{dir, "doc"}]).

Documenting module:

%%% @doc Mathematical algorithms.
%%% <p>Compututing prime numbers, greater common denominator and factorial</p>
%%% @author J. Random

If you put your name in the header of module you will feel responsible for the code and a few words describing the module force me to rethink what I supposed to do.

For exported method:

%% @spec factorial(MaxNumber, FactorialSoFar) -> integer() 
%% @doc Find factorial for given number.

While documenting methods I found useful to define result type and meaningful names for parameters. It’s worth to note that first sentence will be used as short description of method, and paragraphs should be defined by <p> rather than simple line breaks.

The documentation about edoc tags is easy to find. But I have to read whole internet twice and spend some time with Erlang REPL to discover working combination of parameters.

Have fun generating Erlang docs 🙂

Posted in Erlang | Leave a comment

Nauka myślenia w języku Erlang

Odkurzyłem trochę moją umiejętność posługiwania się językiem Erlang poprzez rozwiązywanie problemów z Projektu Euler. Dotąd skupiałem się na jego wsparciu dla wymiany komunikatów, systemów rozproszonych czy też na wzorcach komunikacji (behaviours).

Doskwierała mi świadomość braku umiejętności pisania zgodnie z duchem tego języka. Uparcie chciałem przenieść moje doświadczenia z Java:

  • napisałem własnego eunit’a … takiego bardziej JUnit’owego,
  • ukrywałem odwołania do rekordów, których składnia wydawała mi się … nieczytelna
  • oszczędzałem na guardach w metodach,
  • ograniczałem list comprehension i higher-order functions by nie zaciemniać algorytmów,
  • rekurencja raczej przypominała pętle foreach w Java,
  • foldl, trzymanie danych w listach wydawało się … krępującą zaszłością.

Gdy zakres języków programowania ograniczał się do Java, C++, C#, Pascal wszystkie wydawały się podobne (w końcu należą do tej samej rodziny). W każdym stosuje się analogiczne podejścia (proceduralność lub obiektowość). Odmienność VBA i PHP spisywałem na karb specyficznych zastosowań tych języków.

Wystarczy, jednak poznanie czegoś diametralnie innego jak Erlang czy Scheme i świat przestaje być tak prosty 🙂 Przychodzi świadomość, że nowy język to nie zawsze tylko składnia + biblioteka standardowa + inne skróty klawiszowe w IDE. Czasem nowy język to zupełnie inny sposób myślenia.

Rozwiązywanie czysto algorytmicznych problemów z projektu Euler pozwoliło mi na eksperymenty z różnymi konstrukcjami Erlanga. Dzięki temu zaczynam wyrabiać sobie umiejętność programowania Erlangiem w Erlangu (a nie poprzestaję na używaniu Erlanga jak Java):

Guardy w metodach zaczynają mi się podobać:

is_prime(2) -> true;
is_prime(N) when N rem 2 =:= 0 -> false;
is_prime(N) -> is_prime(N, 3).

is_prime(N, Div) when N rem Div =:= 0 -> false;
is_prime(N, Div) when Div * Div > N -> true;
is_prime(N, Div)  -> is_prime(N, Div + 2).

Sposób wykorzystania metod ze standardowej biblioteki wydaje mi się coraz bardziej elegancki:

is_palindrom(N) ->
  Digits = integer_to_list(N),
  Digits =:= lists:reverse(Digits).

foldl? list comprehension? czemu nie:

multiply_digits(Int) ->
  IntAsString = integer_to_list(Int),
  ListOfDigits = [ Char - $0 || Char <- IntAsString],
  MultiplyFun = fun(X, Product) -> X * Product end,
  lists:foldl(MultiplyFun, 1, ListOfDigits).

Doceniam też, drobne ułatwienia jakie daje programiście środowisko, np. sposób obsługi liczb. Nie powinniśmy zawracać sobie głowy przełączaniem się na API obsługi dużych liczb – to niskopoziomowy szczegół implementacji a nie problem biznesowy:

TwoToPower1000 = math:pow(2, 1000),

(Tu chciałem wkleić fragment wykorzystujący higher order function w algorytmie sita Eratostenesa … ale zmieniłem ok 3 w nocy podejście w algorytmie i się nie zachował. Wiadomo jak to jest w nocy po kawie 🙂

Z niektórymi elementami nie przeszedłem jeszcze do porządku dziennego. Mogę zaakceptować, składnię testów:

-module(euler16_tests).
-include_lib("eunit/include/eunit.hrl").

should_sum_digits_in_power_of_2_test() ->
  26 = euler16:sum_of_digits_in_2_to_power(15).,

ale brak zielonego paska poprawiającego nastrój? Konieczność restartu Eclipse po niektórych czynnościach … Cóż może po prostu trzeba nauczyć się Emacs’a 🙂 Oprócz rozwiązywania problemów czysto matematycznych planuję:

… jak czas pozwoli 🙂

Posted in Uncategorized | Tagged , | Leave a comment

Na co komu … przyszłość

Zastanawia mnie sposób w jaki warto świadomie kształtować swoją karierę. Czy wystarczy skupić się na kilku technologiach, które się dobrze zna?

Czy jest sens rozwijać się, podążać za nowościami, jeszcze potężniejszymi językami programowania, jeszcze efektywniejszymi metodykami tworzenia oprogramowania, jeszcze sprawniejszymi systemami kontroli wersji?

Nowe oznacza niesprawdzone czyli bardziej pracochłonne oraz potencjalnie bardzo ryzykowne. Trudniejsze w utrzymaniu i pozyskiwaniu zasobów.

Czy nie lepiej powiedzieć sobie STOP. Zacznijmy myśleć o tym w jaki sposób efektywnie zarabiać pieniądze na swojej wiedzy. Jeśli już inwestować czas to w te umiejętności, które są opłacalna a nie tylko fajne?

Czas jest ograniczony i nie dosyć, że nie można poznać wszystkiego, to na dodatek inwestując czas w ciekawe rzeczy ponosimy koszt alternatywny nie inwestowania w korzystne finansowo rzeczy.

Posted in Uncategorized | 1 Comment

Kontekst a prawda w IT

Istnieje odpowiedź świetnie pasująca do bardzo wielu pytań dotyczących tworzenia oprogramowania. Brzmi ona “To zależy”. Jej użycie może ustrzec człowieka przed wyjściem na idiotę przed kolegami z innej firmy. Nawet wówczas jeśli wypowiadamy się ze 100% przekonaniem na tematy, które dogłębnie poznaliśmy przez długie doświadczenie.

Prawda w IT zależy od wielkości projektu
Czy czytelność kodu i umiejętność zachowania luźnego powiązania komponentów ma jakieś większe znaczenie przy tworzeniu małych projektów przez jedną osobę?

Prawda w IT zależy od rynku, dla jakiego tworzone jest rozwiązanie
Efektywność operowania na zasobach i tworzenia obiektów ma nieco inne znaczenie przy tworzeniu sexy aplikacji na Androida i przy produkcji smutnych rozwiązań korporacyjnych w Java EE.

Prawda w IT zależy od etapu realizacji projektu
“Refaktoryzacja” w logu systemu kontroli wersji niesie inny ładunek emocjonalny dla team leadera sprawdzającego, co się zmieniło w kodzie dzień przed realeasem, oraz dla kolegi z zespołu, z którym dyskutowałeś poprzedniego dnia o problemach jakie masz z jego modułem.

Prawda w IT zależy od tego, jakich narzędzi używamy
Z punktu widzenia użytkownika Eclipse, różnica między CVS a SVN jest często zaniedbywalnie mała. Plugin Eclipse do obsługi CVS implementuje wiele funkcjonalności realizowanych automatycznie przez SVN. Dodatkowo klienci do SVN często nie wykorzystują możliwości drzemiących w SVN (jak choćby zmiana nazwy).

Prawda w IT zależy od platformy
Problem doboru technologii nie dotyczy tego samego w świecie .NET i Java.

Prawda w IT zależy od języka
W oku programisty używającego funkcyjnych języków programowania można dostrzec błysk na dźwięk rekurencji lub zapamiętywania stanu. Podobnie jeśli chodzi o wzorce projektowe i programistów OOP. Gdyby jednak zamienić te osoby miejscami pewnie terminy były by dla nich mniej interesujące.

Prawda w IT zależy od wiedzy o narzędziach
Ostatnio podczas rozmowy ze znajomym, przekonałem się iż mamy zupełnie inne problemy w wykorzystaniu Spring. Wynikało to stąd, iż ja rozpoznałem kilka technik i udało mi się je połączyć dosyć sensownie w określony sposób. On natomiast znał inne i siłą rzeczy łączył je inaczej.

Zapewne listę można wydłużyć. Kontekst ma duży wpływa na opinie w zakresie tworzenia oprogramowania. Dlatego warto udzielać odpowiedzi dopiero wówczas, gdy rozumiemy czego naprawdę dotyczą.

Posted in Uncategorized | Leave a comment