Monthly Archives: February 2013

Link

Naming and Layout Guideliness

Muy buena guía para saber cómo hay que bautizar a las cosas. Estoy de acuerdo en casi todo. Lo de los guiones bajos en los atributos privados siempre me toca las narices. ¿Los ponemos o no los ponemos? He ahí la jodienda.

Advertisements

Principios SOLID

S – Single Responsibility Principle.

Una clase debería tener una única responsabilidad, bien definida. De esta manera solamente debería haber un motivo por cambiar su implementación.

Por ejemplo, una clase Persona nunca debería ser responsable de guardarse en un archivo a sí misma. Por lo tanto, un método Persona.Save(Stream output) viola este principio. En este caso, la clase Persona no solamente tendría como responsabilidad albergar los datos relacionados con la entidad correspondiente, sino también saber cómo guardarla. Tendría, por tanto, al menos 2 motivos para cambiar:

  • si la entidad Persona cambia
  • si los detalles del guardado (por ejemplo, el formato) cambian.

O – Open / Closed Principle

El comportamiento de una clase debería poderse modificar sin tener que cambiar su implementación.

Esto significa que la clase debe ser extensible, pero a la vez, robusta: no queremos tener que andar toqueteando todos sus métodos cada vez que una especificación cambie.

L – Liskov Substitution Principle

Una clase base debe ser SUSTITUIBLE por cualquiera de sus clases derivadas sin producir ningún efecto indeseado.

I – Interface Segregation Principle

Las interfaces deben ser reducidas. Deben permitir una granularidad alta.  Las interfaces grandes y difíciles de implementar SON CONTRAPRODUCENTES.

Es preferible tener varias interfaces pequeñas que una grande. Implementarlas será mucho más fácil y el diseño tendrá más sentido. Aparte de eso, al usar interfaces pequeñas nos forzamos a definir claramente los requisitos y las dependencias de cada método.

D – Dependency Inversion Principle

Trata de depender de abstracciones, no de implementaciones.

Cuando dependemos de abstracciones, tenemos claro qué es lo que necesitamos, aunque no definimos una forma específica de hacerlo. Una interfaz, ReproductorMusica puede tener el método Play. Sabemos qué es lo que hace, pero no cómo lo hace. Esto nos brinda una gran flexibilidad y dota al código de más capacidad semántica.

How to avoid code duplication. Case 1.

As a Clean Code lover, I really hate duplicated code.

In his homonymous book, Robert C. Martin states

”duplication may be the root of all evil in software”

And I think he IS absolutely right.

You better keep your code away from clones… or they will come back to you in the shape of a muddy monster that, for sure, will eat you.

I personally came to a situation in which I find it difficult to avoid repeating myself. This is the case:

I have a method that makes some calculations in a 2D plane (for layout and runtime design). This method takes some lengths and some points and make the necessary modifications to a control in order to a 2D transform to it. Let’s see a real sample in C#:

  • This sample method is really random, but it demostrates the problem. Each version works with one dimension, vertical or horizontal. Essentially both methods do the same, and the structure is almost identical. How to simplify these 2 into ONE?

For horizontal values:

        public static Rect DoSomethingCoolHorizontal(Rect inputRect)
        {

            var multiplyHorzPositions = inputRect.Left * inputRect.Width;

            Rect p = new Rect();

            p.Left = multiplyHorzPositions;
            p.Top = inputRect.Top;

            p.Width = multiplyHorzPositions;
            p.Height = inputRect.Height;            

            return p;
        }

For vertical values:

        public static Rect DoSomethingCoolVertical(Rect inputRect)
        {
            var multiplyVerPositions = inputRect.Top * inputRect.Height;

            Rect p = new Rect();

            p.Left = inputRect.Left;
            p.Top = multiplyVerPositions;

            p.Width = inputRect.Width;
            p.Height = multiplyVerPositions;

            return p;
        }

You may notice those 2 methods are 95% the same. But each one calculates the values for each axis. Vertical and horizontal use different properties.

The duplication in these kind of methods, follow  this simple rule:  when in the first method it says “left”, in the second it says “top”. As expected, “width” is replaced by “height”. The same would have been applicable for x and y.

In the case above, the rest is exactly the same, except for the return, that reverses the order of the Point arguments). They are so repetitive that even a text search&replace would do the job in order to replicate the same calculations for the other axis.

To clarify the method, you have to take into account that the structs that carry the properties are Rectangles with 4 attributes, that actually are 2 (2 for each dimension).

I tried to keep it beautiful, but I failed miserably again and again. That’s why I asked myself to Uncle Bob. I hope he could shed a bit light on this.

EDIT

Attention: Robert Martin did his magic again! I asked him in Twitter and has been so considerate and kind that he rewrote the sample in Java with the duplication totally eliminated. He provided a link with the source code. Just see the comments section.

WOW.

The solution to this lies in making a method that swaps vertical / horizontal dimensions. The call to the Vertical derivative is the Horizontal one with 2 swaps (argument and result):

        public static Rect DoSomethingNonDuplicateVertical(Rect inputRect)
        {
            return DoSomethingCoolHorizontal(inputRect.Swap()).Swap();
        }

With the Swap method being:

        public Rect Swap()
        {
            return new Rect(Top, Left, Height, Width);
        }

The constructor of the Rect class is:

  public Rect(double left, double top, double width, double height)
        {
            Left = left;
            Top = top;
            Width = width;
            Height = height;
        }

Now IT REALLY DOES SOMETHING COOL.

Thanks, Uncle Bob. Great tip!

protected override void Update(GameTime gameTime)
{
    newState = Keyboard.GetState();
    newPadState = GamePad.GetState(PlayerIndex.One);

    if (inDialog && dialogNPC != null)
    {
        dialog.Show();
        dialogNPC.StartDialog(dialogNPC.DialogName);
        dialogNPC = null;
    }

    if (!dialog.Enabled)
    {
        inDialog = false;
        dialogNPC = null;
    }

    if (activeScreen == startScreen)
    {
        HandleStartScreenInput();
    }
    else if (activeScreen == helpScreen)
    {
        HandleHelpScreenInput();
    }
    else if (activeScreen == createPCScreen)
    {
        HandleCreatePCScreenInput();
    }
    else if (activeScreen == quitPopUpScreen)
    {
        HandleQuitPopUpScreenInput();
    }
    else if (activeScreen == genderPopUpScreen)
    {
        HandleGenderPopUpScreenInput();
    }
    else if (activeScreen == classPopUpScreen)
    {
        HandleClassPopUpScreenInput();
    }
    else if (activeScreen == difficultyPopUpScreen)
    {
        HandleDifficultyPopUpScreenInput();
    }
    else if (activeScreen == nameInputScreen)
    {
        HandleNameInputScreenInput();
    }
    else if (activeScreen == introScreen)
    {
        HandleIntroScreenInput();
    }
    else if (activeScreen == creditScreen)
    {
        HandleCreditScreenInput();
    }
    else if (activeScreen == actionScreen)
    {
        HandleActionScreeenInput();
        HandlePlayerInput(gameTime);
    }
    else if (activeScreen == viewCharacterScreen)
    {
        HandleViewCharacterScreenInput();
    }
    else if (activeScreen == quitActionScreen)
    {
        HandleQuitActionScreenInput();
    }
    else if (activeScreen == combatScreen)
    {
        HandleCombatScreenInput();
    }
    else if (activeScreen == treasureScreen)
    {
        HandleTreasureScreenInput();
    }
    base.Update(gameTime);

    oldState = newState;
    oldPadState = newPadState;
}

image