Showing posts with label design patterns. Show all posts
Showing posts with label design patterns. Show all posts

Monday, January 31, 2022

Ports and Adapters part 1

Ports and Adapters part 1

A love story

Today I want to begin a series about a technique which has helps to isolate code from changes in implementation details.  Sometimes those details may seem central to the code in question, but many times it just isn't.

I'm going to start with a little story.  I don't know if it's interesting or not, but it's all true.3

Many years ago, back when my razor stubble was brown and I was a hired gun programmer, I was responsible for maintaining a corporate security library.  This was an important library.  Dozens of applications depended on it to authenticate and authorize corporate users.

There's just one problem:  It relied on an obsolete LDAP library which was no longer being maintained, and that had to change.

This was a pretty frightening task.  It absolutely had to work.  This was before the idea of providing such a thing as a service had become popular, and the library was statically linked to all those applications.  A mistake meant a patch, a patch that would get attention, and what's more a patch that would have to be quickly rolled out to all affected applications.

I hate attention.  Well, bad attention anyway.  I suppose I'm also not really a big fan of emergency deployments. 

I had to formulate a plan of attack.  As per my usual practice, I banged my head against things until I eventually worked out what I could have found in a book.  Let me walk you through my process:

Step 1:  Tests

First, I built a comprehensive suite of unit tests that validated all of the library's functions.  The tests were a little more integration-ey than I would build today, but when things were green?  I had great confidence that I would have a successful build.  We were also adopting Jenkins at the time, so every code commit was tested and built automatically.

Step 2: Isolation

Next up, I isolated all of the library calls behind interfaces.  That was a lot of effort, but with the tests backing me up, I was able to keep everything working just as before.  I did have to add a factory to instantiate the main library class, but kept that hidden behind a facade that looked unchanged to the API users. 

Step 3: In with the new

Now I started on the new code.  By sticking to the interfaces which allowed me to accomplish step 2, I was able to swap back and forth between the fully functional production implementation and the one under development.  All I had to do was change the name of the class from 'new obsoleteImplemenation()' to 'new unfinishedImplementation()'.  I could run the test suite and get a pretty solid idea of how far I had come and how far I had to go.

Step 4: Risky business

I realized that this was dangerous territory.  If I shipped a library that had an unexpected bug under load or under some kind of unexpected error condition, there could be really big implications.  The user base included both internal and external entities, offering lots of exposure.  That was too big a risk, so I had to do something to mitigate it.

Step 5: On reflection, this is a good idea

I was working in Java, so I decided to take advantage of reflection to create the class.  If you aren't familiar with the concept, it is just a way of creating an object using the name of the class as a string value.  That came in handy, because now I could just have the two different class names for the now-isolated library and use either one live at run-time.  To make it as safe as possible, I initially defaulted to the old library, but gave the clients an option to set an environment variable to enable the new one.

Fortunately, I had good ties with developers for several of the other projects, and I was able to cajole them into testing and then deploying with the optional library enabled.

Step 6: Once more into the breach

Once I had good feedback, I felt safe making the new library the default, while allowing an environment variable to enable the old library.  I kept that around for a good long while, until I was sure it was safe to get rid of it.

Phew

That was a lot of effort and a lot of worry as well. We can do a little better than this.  In fact, we can do a great deal better than this, although frankly I was proud of my accomplishment.  What I'd stumbled into was a more general idea around dependency isolation.  You'll hear terms like 'hexagonal architecture' used to tell you what to do.  

But how do you actually DO it?

That's what I want to talk into in the next few posts.  Swapping dependencies can be a giant pain point, but it does not have to be.  The most difficult thing, really, is adapting how you think about systems.  The trick is to stop trying to adapt our code to someone else's idea of how an API should work.  That is OK for quick demonstrations or tutorials, but it's not how I believe we should build systems.

Instead, when we require a capability, we should design an API for that capability ourselves, whether or not we intend to implement it.  The design should be harmonious with our existing system, or at least follow similar conventions.  It should not feel tacked on.

Integrating external libraries deeply into our own code base is a code smell.  I want my code talking to my own libraries, which will act as adapters to the third party code I want or need to use.  Let those adapters have the weird stuff that thinks the way other people do.  My job is to make those adapters conform to the expectations I've set for/with my API design.

Next time around, I'll dig a little deeper into what I mean by designing an API ourselves, and what the benefits (and costs) are.

Peace,

JD

Friday, November 13, 2015

Design Patterns: Singleton

The word 'singleton' is used to describe a design pattern for objects where only one instance of the object is allowed to exist.

It would technically be possible for you to use nearly any Java class as a singleton by being very, very careful to only create one instance and use it throughout your application.  But why do you want to work that hard?  It's pretty easy to force a class to act as a singleton with a few lines of code.

First of all, we make sure that there is no constructor available to the public.  This means we must create at least one constructor and mark it with the keyword private.  This will keep random objects from just creating their own instances of the class you wish to protect.

    public class MySingletonClass {
        private MySingletonClass() {}
    }

Of course, that means you can't create your single object either!  Never fear, we can get around that.  After all, the singleton class itself can still invoke the constructor.  Since we can't make an instance, though, it will have to be a static method.  I prefer to call it 'getInstance' because that's a good description of what it does.

Of course, it's not safe or desirable to have getInstance just create a new object each time it's called.  So instead we'll store an object reference of the classes' own type inside it and use that:

    public class MySingletonClass {
        private MySingletonClass() {}
        private static MySingletonClass singleton = null;
        ....
        public static MySingletonClass getInstance() {
            ....
            return singleton;
        }
    }

And the rest is just the work that needs to be done to make this function reliably:

     public class MySingletonClass {
        private MySingletonClass() {}
        private static MySingletonClass singleton = null;
        ....
        public static MySingletonClass getInstance() {
            if (null == singleton) {
                singleton = new MySingletonClass();
            }
            return singleton;    
        }
    }

So what have we done here?  We've created a class that can't just be instantiated by anyone.  Instead, we've enforced a contract on client classes, one that states they can only create one copy of this object.  Whichever object attempts to obtain the singleton first will (unknowingly) be responsible for creating it as well in a case of lazy initialization.  The getInstance method is our gatekeeper for this, handing out the same object reference whenever asked:

    MySingletonClass aSingleton = MySingletonClass.getInstance();

As you can see, using it is pretty easy.  You could of course provide parameters to this method and pass them on to the constructor if needed.

Friday, December 5, 2014

Encapsulation - Protecting You From Yourself

Encapsulation sounds like the central plot point of some weird science fiction movie, but actually it's a fundamental technique for improving the reliability and predictability of object oriented programs. Let's pull out our old friend PersonData and see what's ailing him today: 

public class PersonData { 
    public String givenName; 
    public String surName; 
   
    public PersonData() { } 

    public PersonData(String given, String sur) { 
        givenName = given; 
        surName = sur; 
    } 

    public String toString() { 
        return givenName + " " + surName; 
    } 


Let me start by saying that there is nothing fundamentally wrong with this class. It is properly formed, is created with a valid constructor, and has a fully functional toString override method that formats the name is a reasonable manner. Is it object oriented? Well, it's an object at any rate, but we can do better.

 Here's the key issue I'd like to address today. Suppose we had a method that we wanted to use to ensure that a person's name begins with a capital letter. The logic for this is simple enough, but I'm going to deliberately break it for this example, so don't use this method as-is:

public static String ensureFirstCharacterIsUpperCase(String input) { 
    String upperCaseString = null; 
    if (Character.isUpperCase(input.charAt(0))) { 
        return input; 
    } 
    upperCaseString = input.substring(0,1).toUpperCase() + input.substring(1); 
    return input; //Bad programmer! No Pizza! 


 Ignoring for the moment the fact that this method will not quite do what we actually want, how would one go about using it? Well, we could go ahead and write something like this:

PersonData joeBlow = new PersonData(); joeBlow.givenName=ensureFirstCharacterIsUpperCase("joe"); joeBlow.surName=ensureFirstCharacterIsUpperCase("blow"); 
System.out.println(joeBlow); 

That would work just fine, the first, second, and eight hundredth time you do it. But why would you want to? What if you decided that you wanted to capitalize all surnames? What if you wanted to plug in some library that understands how to capitalize special names like MacNeil or something? Do you really want to search out every instance of setting the last name and change it?

There is a better way, my friend, and it gets to one of the other key aspects of object oriented programming. Encapsulation is basically protecting your data from your own programs by making it inaccessible except through a very clearly defined path that you strictly control.

We start by making the fields surName and givenName private. This indicates that they cannot be changed by code in any class except PersonData.

private String givenName; 
private String surName; 

Of course, we aren't quite finished at this point, because we can't change or even see these values. That could make for a remarkably useless class if we did not find a way around it. The way around it is by creating 'accessor' and 'mutator' methods. Those are horrible names so we usually just call them 'getters' and 'setters'. These labels actually make a lot more sense, because the naming convention is to prefix the field name with 'get' and 'set'. 

The required methods for PersonData could look like this:

public void setGivenName(String newGivenName) { 
    givenName = newGivenName; 


public String getGivenName() { 
    return givenName; 


public void setSurName(String newSurName) { 
    surName = newSurName; 


public String getSurName() { 
    return surName; 


 With the fields marked private, and with our getters and setters in place, we would now write the code above more like this:

 PersonData joeBlow = new PersonData(); 
 joeBlow.setGivenName (ensureFirstCharacterIsUpperCase("joe")); 
 joeBlow.setSurName (ensureFirstCharacterIsUpperCase("blow")); 
 System.out.println(joeBlow); 

 But wait, there's more!

 Why should we take a chance that we (or some other developer) fails to use that method to make sure our first character is correct? We can do better than that, and make sure it always happens no matter what kind of night someone had. For now, we'll move 'ensureFirstCharacterIsUpperCase' into PersonData (we'll talk about a better way later) and we'll change our setters a bit:

public void setGivenName(String newGivenName) { 
    givenName = ensureFirstCharacterIsUpperCase(newGivenName); 


public void setSurName(String newSurName) { 
    surName = ensureFirstCharacterIsUpperCase(newSurName); 


 With this done, we can now do this instead:

 PersonData joeBlow = new PersonData(); 
 joeBlow.setGivenName ("joe"); 
 joeBlow.setSurName ("blow"); 
 System.out.println(joeBlow); 

 And still get everything looking the way we expect. We've minimized the amount of code we need to write, and we've encapsulated both field access and a bit of business logic in our PersonData class. Malicious or misinformed programmers will not be able to bypass our hard and fast rules. IDEs won't even let you see the fields in their helpful pop-up dialogs. Dogs will love us.

Of course, we can (and really should) do more than this. First off, we have a constructor that accepts the two names, and we need to make sure it's also performing this task. That's probably most easily done by also having it call the setters. Second, we can think about other business rules we can enforce, like "A user's first name cannot be null or zero length". The setter can check for this and refuse to accept a bad value. Of course, if it does so it also needs to complain to the caller that they've done it wrong. This would be done via an Exception, which we'll need to talk about soon.

Get used to writing your code this way. Protect your data from yourself. Use accessor and mutator methods regularly and uniformly. It's a small amount of extra work, but you will thank yourself later. It's actually a VERY small amount of extra work, given that any decent IDE will create the methods for you if you just ask, and it will certainly save you time and energy later on.

I'll update this post later with a link to a gist containing the fully updated PersonData Object.

Wednesday, December 3, 2014

Diamonds are not an Object's Best Friend

Once again, I turn to a post I made on reddit for inspiration.

Some people wonder why inheritance branch but never join.  Why not tie two different families of objects together and extend both of 'em?

I can see the point of that, one could conceivably build objects that join several disparate branches of inheritance into large and versatile classes.  Conceivably.  One could also get oneself into a world of trouble, though, and this is most easily explained via the so-called diamond inheritance problem.

Please note that you cannot do this and expect your code to compile, it's just a conceptual demonstration.
Diamond.java
public class Diamond extends Class1, Class2 {
}

Class1.java
public class Class1 {
    public void doSomething() {
         System.out.println("Class1.doSomething()");
    }
}

Class2.java
public class Class2 {
    public void doSomething() {
         System.out.println("Class2.doSomething()");
    }
}
Now you have a main that uses the above classes:
public static void main(String[] args) {
    Diamond diamond = new Diamond();
    diamond.doSomething();
}
So, when you run main, what happens? This is the essence of the diamond problem. You wind up having two lines of inheritance converging on one and run into a quite serious issue.

This basically just scratches the surface of the problem, of course.  We can also think about what happens when you try to invoke something in super.  The question would have to become 'which super'?  There are many potential negative ramifications from the interactions that could be created by allowing this kind of inheritance, and the creators of Java decided that the best way to avoid them was by preventing the issue altogether.
One can achieve most of the positive effects of multiple inheritance via Interfaces and a few helpful design patterns such as Adapter, Composite and Decorator.