Posts tagged ‘inheritance’

Inheritance as an API

2013-09-15 22:46

Saying “API” nowadays is thought first and foremost to refer to a collection of HTTP request handlers that expose data from some Web service in a machine-readable format (usually JSON). This is not the meaning of API I have in mind right here, though. The classic one talks about any conglomerate of programming language constructs – functions, classes, packages – that are available for us to use.

You can interact with an API in numerous different ways, but one of them is somewhat less common. Occasionally, besides having functions to call and objects to create, you are also presented with base classes to inherit. This is symptomatic to more complex libraries, written mostly in statically typed languages. Among them, the one that takes the most advantage of this technique is probably Java.

Note that what I’m talking about here is substantially different from implementing an interface. That is a relatively common occurrence, required when working with listeners and callbacks:

  1. private OnClickListener mHelloButtonClickListener = new OnClickListener() {
  2.     @Override
  3.     public void onClick(View v) {
  4.         Toast.makeText(getContext(), "Hello world!", Toast.LENGTH_SHORT).show();
  5.     }
  6. };

as well as in many other situations and patterns. There is nothing terribly special about it, given that even non-object oriented languages have equivalent mechanisms of accomplishing the same objective: separating the “how” from “what” in code, possibly to exchange or expand the latter.

Inheriting from a base class is something else entirely. The often criticized, ideologically skewed interpretation of OOP would claim that inheritance is meant to introduce more specialized kinds of existing types of objects. In practice, that’s almost completely missing the real point.

What’s important in creating a derived class is that:

  • like with interface, it needs to override certain methods and fill them with code
  • but also, it may do so using a unique, additional API

Combined, these two qualities allow to introduce much more sophisticated ways of communication between the API and its client code. It’s a powerful tool and should be used sparingly, but certain types of libraries and (especially) frameworks can benefit greatly from it.

As an example, look at the Guice framework for dependency injection. If you use it in your project, you need to configure it by specifying bindings: mapping between the interfaces used by your code and their implementations. This is necessary for the framework can wire them together, possibly in more then one way (different in production code and in test code, for example).
Bindings are quite sophisticated constructs. For non-trivial applications – and these are the ones you generally want to apply dependency injection to – they cannot really be pinned down to a single function call. Other, similar frameworks would therefore use completely external configuration files (usually in XML), which has a lot of downsides.

Guice, however, has a sufficiently smart API that allows to realize all configuration in actual, compilable code. To achieve this, it uses inheritance, exactly as described above. Here’s a short sample:

  1. public class BillingModule extends AbstractModule {
  2.     @Override
  3.     protected void configure() {
  4.       bind(TransactionLog.class).to(DatabaseTransactionLog.class);
  5.       bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
  6.     }
  7. }

Overridden methods in the above class (well, one method) serve as “sections” in the “configuration file”. Inherited methods, on the other hand, provide an internal, limited namespace that can be used to compose configuration “entries”. Since everything is real code, we can have the compiler check everything for some basic sanity as well.

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

Być albo nie być

2008-04-18 20:08

W programowaniu relacja ‘jest’ najczęściej oznacza możliwość potraktowania pewnej wartości (lub ogólniej: obiektu) jako należącego do określonego typu (klasy). Od kiedy wynaleziono dziedziczenie, obiekty mogą być polimorficzne – czyli być traktowane tak, jakby należały do kilku różnych typów danych. Dwa proste przypadki obejmują: zwykłe dziedziczenie publiczne, gdy obiekt klasy pochodnej jest też obiektem klasy bazowej, oraz implementowanie abstrakcyjnych interfejsów.

Czasami jednak to, co w związku ze słówkiem ‘jest’ bywa intuicyjne, nie zawsze sprawdza się w praktyce. Tak jest chociażby wtedy, gdy bierzemy pod uwagę:

  • Dziedziczenie prywatne. Ten rzadko spotykany typ dziedziczenia (obecny w C++) sprawia, że choć obiekt klasy pochodnej jest nadal obiektem klasy bazowej, to wie o tym tylko on sam. Dlatego nie jest możliwe bezproblemowe rzutowanie w górę, które dla dziedziczenia publicznego jest przeprowadzane automatycznie. Mimo tego nadal mamy do czynienia z normalnym dziedziczeniem i jeśli przy użyciu jakichś brzydkich sztuczek (czyli na przykład reinterpret_cast) skonwertujemy odwołanie do obiektu na odwołanie do klasy bazowej, będziemy mogli z niego poprawnie skorzystać.
  • Kolekcje obiektów.Teoretycznie zbiór obiektów typu B jest też zbiorem obiektów typu A, jeśli typ B jest rodzajem typu A (np. klasą pochodną). W praktyce jednak kolekcja może się zmieniać, a przy traktowaniu jej w sposób bardziej ogólny istnieje możliwość dodania do niej obiektów będących wprawdzie typu A, ale nie mających nic wspólnego z B.
    Stąd też wynika fakt, że w językach programowania nie można ot tak sobie rzutować pojemników na takie, które przechowują “bardziej ogólne” obiekty. Jeśli więc chcemy zmienić np. List<string> na List<object> w C#, musimy świadomie wykonać kopię pojemnika.
  • Niewirtualne wielodziedziczenie. W bardziej skomplikowanych przypadkach, gdy klasa ma więcej niż jedną klasę bazową, mogą się pojawiać problemy z niejednoznacznością. Jeżeli bowiem idziemy w górę więcej niż dwa pokolenia, może mieć znaczenie ścieżka w grafie dziedziczenia, którą przy okazji obieramy. Ten problem eliminuje dziedziczenie wirtualne, przy okazji wprowadzając jednak spory zestaw nowych kłopotów. I dlatego właśnie wielodziedziczenia powinno się nie unikać, o ile się nie wie dokładnie, co chce zrobić :)

Zatem proste, zdawałoby się, stwierdzenie “coś jest jakiegoś typu”, w programowaniu może wcale nie być takie oczywiste. Takie są aczkolwiek uroki naszego ulubionego OOP-u :]

Tags: , ,
Author: Xion, posted under Programming » Comments Off on Być albo nie być
 


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