Posts tagged ‘methods’

Fluent Chaining

2013-06-30 14:24

Look at the following piece of jQuery code:

  1. $('span')
  2.     .attr('data-type', 'info')
  3.     .addClass('message')
  4.     .css('width', '100%')
  5.     .text("Hello world!")
  6.     .appendTo($('body'))
  7. ;

Of the two patterns it demonstrates, one is almost decisively bad: you shouldn’t build up DOM nodes this way. To get more concise and maintainable code, it’s better to use one of the client-side templating engines.

The second pattern, however, is hugely interesting. Most often called method chaining, it also goes by a more glamorous name of fluent interface. As you can see by a careful look at the code sample above, the idea is pretty simple:

Whenever a method is mostly mutating object’s state, it should return the object itself.

Prime example of methods that do that are setters: simple function whose pretty much only purpose is to alter the value of some property stored as a field inside the object. When augmented with support for chaining, they start to work very pleasantly with few other common patterns, such as builders in Java.
Here’s, for example, a piece of code constructing a Protocol Buffer message that doesn’t use its Builder‘s fluent interface:

  1. Person.Builder builder = Person.newBuilder();
  2. builder.setId(42);
  3. builder.setFirstName("John");
  4. builder.setLastName("Doe");
  5. builder.setEmail("johndoe@example.com")
  6. Person person = builder.build();

And here’s the equivalent that takes advantage of method chaining:

  1. Person person = Person.newBuilder()
  2.     .setId(42)
  3.     .setFirstName("John")
  4.     .setLastName("Doe")
  5.     .setEmail("johndoe@example.com")
  6.     .build();

It may not be shorter by pure line count, but it’s definitely easier on the eyes without all these repetitions of (completely unnecessary) builder variable. We could even say that the whole Builder pattern is almost completely hidden thanks to method chaining. And undoubtedly, this a very good thing, as that pattern is just a compensation for the deficiencies of Java programming language.

By now you’ve probably figured out how to implement method chaining. In derivatives of C language, that amounts to having a return this; statement at the end of method’s body:

  1. jQuery.fn.extend({
  2.     addClass: function( value ) {
  3.         // ... lots of jQuery code ...
  4.         return this;
  5.     },
  6.     // ... other methods ...
  7. });

and possibly changing the return type from void to the class itself, a pointer to it, or reference:

  1. public Builder setFirstName(String value) {
  2.     firstName_ = value;
  3.     return this;
  4. }

It’s true that it may slightly obscure the implementation of fluent class for people unfamiliar with the pattern. But this cost comes with a great benefit of making the usage clearer – which is almost always much more important.

Plus, if you are lucky to program in Python instead, you may just roll out a decorator ;-)

Tags: , , , , , ,
Author: Xion, posted under Programming » Comments Off on Fluent Chaining

How Does this Work (in JavaScript)

2012-03-18 21:19

Many caveats clutter the JavaScript language. Some of them are quite hilarious and relatively harmless, but few can get really nasty and lead to insidious bugs. Today, I’m gonna talk about something from the second group: the semantics of this keyword in JavaScript.

Why’s this?

It is worth noting why JS has the this keyword at all. Normally, we would expect it only in those languages which also have the corresponding class keyword. That’s what C++, Java and C# have taught us: that this represents the current object of a class when used inside one of its methods. It only makes sense, then, to use this keyword in a class scope, denoted by the class keyword – both of which JavaScript doesn’t seem to have. So, why’s this even there?

The most likely reason is that JavaScript actually has something that resembles traditional classes – but it does so very poorly. And like pretty much everything in JS, it is written as a function:

  1. function Greeting(text) {
  2.     this.text = text
  3. }
  4. Greeting.prototype.greet = function(who) {
  5.     alert("Hello, " who + "! " + this.text);
  6. }
  7.  
  8. var greeting = new Greeting("Nice to meet you!");
  9. greeting.greet("Alice");

Here, the Greeting is technically a function and is defined as one, but semantically it works more like constructor for the Greeting “class”. As for this keyword, it refers to the object being created by such a constructor when invoked by new statement – another familiar construct, by the way. Additionally, this also appears inside greet method and does its expected job, allowing access to the text member of an object that the method was called upon.

So it would seem that everything with this keyword is actually fine and rather unsurprising. Have we maybe overlooked something here, looking only at half of the picture?…

Well yes, very much so. And not even a half but more like a quarter, with the remaining three parts being significantly less pretty – to say it mildly.

Tags: , , , ,
Author: Xion, posted under Programming » 2 comments

Trzy rodzaje metod w Pythonie

2011-10-03 23:05

Obiekty mają metody. Tak, w tym stwierdzeniu nie należy doszukiwać głębokiego sensu – jest ono po prostu prawdziwe :) Gdy mówimy o metodach obiektów czy też klas, zwykle mamy jednak na myśli tylko jeden ich rodzaj: metody instancyjne. W wielu językach programowania nie jest to aczkolwiek ich jedyny rodzaj – z takim przypadkiem mamy do czynienia chociażby w Pythonie.

Jak zawsze metody instancyjne są domyślnym typem, który tutaj jest dodatkowo zaznaczony obecnością specjalnego parametru – self – występującego zawsze jako pierwszy argument. To odpowiednik this z języków C++, C# czy Java i reprezentuje instancję obiektu, na rzecz której wywoływana jest metoda:

  1. class Counter(object):
  2.     def __init__(self, value = 0):
  3.         self._value = value
  4.     def increment(self, by = 1):
  5.         self._value += by

Fakt, że musi być on jawnie przekazany, wynika z zasad tworzenia zmiennych w Pythonie. Nie muszą być one jawnie deklarowane. Dlatego też odwołanie do pola obiektu jest zawsze kwalifikowane, gdyż przypisanie do _value zamiast self._value stworzyłoby po prostu zmienną lokalną.

Istnieją jednak takie metody, które nie operują na konkretnej instancji klasy. Typowo nazywa się je statycznymi. W Pythonie nie posiadają one parametru self, lecz są opatrzone dekoratorem @staticmethod:

  1. @staticmethod
  2. def format_string():
  3.     return "%d"

Statyczną metodę można wywołać zarówno przy pomocy nazwy klasy (Counter.format_string()), jak i jej obiektu (Counter().format_string()), ale w obu przypadkach rezultat będzie ten sam. Technicznie jest to bowiem zwyczajna funkcja umieszczona po prostu w zasięgu klasy zamiast zasięgu globalnego.

Mamy wreszcie trzeci typ, mieszczący się w pewnym sensie pomiędzy dwoma opisanymi powyżej. Nie wydaje mi się jednak, żeby występował on w żadnym innym, popularnym języku. Chodzi o metody klasowe (class methods). Nazywają się tak, bo są wywoływane na rzecz całej klasy (a nie jakiejś jej instancji) i przyjmują ową klasę jako swój pierwszy parametr. (Argument ten jest często nazywany cls, ale jest to o wiele słabsza konwencja niż ta dotycząca self).
W celu odróżnienia od innych rodzajów, metody klasowe oznaczone są dekoratorem @classmethod:

  1. @classmethod
  2. def from_other(cls, counter):
  3.     return cls(counter._value)

Podobnie jak metody statyczne, można je wywoływać na dwa sposoby – przy pomocy klasy lub obiektu – ale w obu przypadkach do cls trafi wyłącznie klasa. Tutaj akurat będzie to Counter, lecz w ogólności może to być także klasa pochodna:

  1. class BoundedCounter(Counter):
  2.     MAX = 100
  3.  
  4.     def __init__(self, value = 0):
  5.         if value > self.MAX:
  6.             raise ValueError, "Initial value cannot exceed maximum"
  7.         super(BoundedCounter, self).__init__(value)
  8.  
  9.     def increment(self, by = 1):
  10.         super(BoundedCounter, self).increment(by)
  11.         self._value = min(self._value, self.MAX)
  12.  
  13.     @classmethod
  14.     def from_other(cls, counter):
  15.         value = min(counter._value, cls.MAX)
  16.         return cls(value)

Powyższy kod – będący przykładem dodatkowego sposobu inicjalizacji obiektu – to dość typowy przypadek użycia metod klasowych. Korzystanie z nich wymaga aczkolwiek nieco wprawy w operowaniu pojęciami instancji klasy i samej klasy oraz ich poprawnego rozróżniania.
W gruncie rzeczy nie jest to jednak nic trudnego.

Tags: , , ,
Author: Xion, posted under Programming » 4 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.