Friday, March 27, 2015

SOLID Review: Interface Segregation Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

The Interface Segregation Principle is probably the most straight-forward of all the SOLID principles. It states:

"Clients should not be forced to depend on methods that they do not use."

In dynamic languages, this isn't really much of an issue because there is no way to define and force the implementation of interfaces on classes (like in Java). Instead, a set of methods determines whether or not an object implements an interface. If an object responds to "a particular set of methods", it has implemented that "particular interface".

In Ruby, modules can be used to define and share sets of methods across multiple classes. Using this construct, we can define different "interfaces". So, when we say "keep our interfaces segregated", we're really saying "keep our modules segregated". This leads to highly cohesive modules.

There are two main benefits to cohesive modules in Ruby: less coupling and more readable code.

Implementing Phones

By keeping our modules small and focused, we are simply applying the Single Responsibility Principle, but for modules. For example, let's create a module called Phone:
module Phone
  def call(number)
    "Calling #{number}..."
  end

  def hangup
    "Hanging up!"
  end

  def text(number, message)
    "Texting '#{message}' to #{number}."
  end
end
Here, we have a set of common behaviors for phones. We can make use of this by including them in our class. Let's create a CellPhone:
class CellPhone
  include Phone
end
Now, our CellPhone class implements the methods in Phone! Any instance of CellPhone can call, hangup, and text other numbers.

Let's create a new class called RotaryPhone:
class RotaryPhone
  include Phone

  # Eek... code smell.
  def text(number, message)
    raise 'Cannot text on this type of phone!'
  end
end
Since we are overriding one of the methods in our module, it's a sign our module isn't cohesive enough. Our RotaryPhone is being littered with methods it does't need!

Another issue worth noting is the tight coupling between our two classes caused by sharing the same, non-cohesive module. Suppose we don't override the text method in RotaryPhone:
class RotaryPhone
  include Phone
end
Any errors caused by text in our Phone module would end up in both classes, even though RotaryPhone doesn't care about text! This tight coupling between CellPhone and RotaryPhone is unnecessary.

Segregate the Modules

A good solution for our problem is to segregate the basic phone behaviors from the mobile phone behaviors:
module BasicPhone
  def call(number)
    "Calling #{number}..."
  end

  def hangup
    "Hanging up!"
  end
end

module MobilePhone
  def text(number, message)
    "Texting '#{message}' to #{number}."
  end
end
Now, each of our classes implement only the modules they require:
class CellPhone
  include BasicPhone
  include MobilePhone
end

class RotaryPhone
  include BasicPhone
end

A Readable, Loosely-Coupled Solution

The behaviors of each class are more clearly defined by the explicitness of the modules it includes. Also, CellPhone and RotaryPhone are only coupled by the methods in BasicPhone, which makes sense since they both require the basic behaviors or call and hangup. Both of our issues above are solved!

Conclusion

Although the Interface Segregation Principle is less important in dynamic languages like Ruby, it still leads to cohesive, readable classes. By keeping modules focused, we end up with looser coupling and cleaner "interface" definitions. They aren't major wins, but wins nonetheless!

Happy coding!

Thursday, March 5, 2015

SOLID Review: Liskov Substitution Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

Barbara Liskov introduced her substitution principle back in 1987 during her keynote titled Data Abstraction and Heirarchy. Today, it is one of the five SOLID principles in object-oriented programming. The original definition is as follows:

"Let q(x) be a property provable about objects x of type T. Then q(y) is provable for objects y of type S, where S is a subtype of T."

Simply put:

"Instances of any type should be replaceable by instances of its subtypes without creating incorrect behaviors."

How can we ensure that our classes abide by the Liskov Substitution Principle? For starters, we must ensure that any subtype implements the interface of its base type. In the world of dynamic languages, this is better stated as a subtype must respond to the same set of methods as its base type.

We must also ensure that methods in any subtype preserve the original promises of methods in its base type. What "promises" are we talking about? For that, we turn to another design principle known as Design by Contract.

Design by Contract

The concept of Design by Contract was coined by Bertrand Meyer in his book Object Oriented Software Construction. It's official description is much more detailed, but to paraphrase, there are three basic principles:
  • Subtypes should not strengthen any preconditions of its base type. That is, requirements on inputs to a subtype cannot be stricter than in the base type.
  • Subtypes should not weaken any postconditions of its base type. That is, the possible outputs from a subtype must be more than or equally restrictive as from the base class.
  • Subtypes must preserve all invariants of its base type. That is, if the base type has guarantees that certain conditions be true, its subtype should make those same guarantees.

If any of the above are violated, chances are the Liskov Substitution Principle is also violated.

A Liskov Substitution Checklist

Let's look at a simple example. We're going to model several types of birds. We'll start by defining a base type called Bird:
class Bird
  def initialize
    @flying = false
  end

  def eat(food)
    if ['worm', 'seed'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end

  def lay_egg
    # The Egg class has a method 'hatch!' that returns a new Bird.
    Egg.new 
  end

  def fly!
    @flying = true
  end
end
Instances of Bird are very simple. They eat only certain types of food, lay eggs, and can go from sitting on the ground to flying in the air. For now, ignore the fact that our Bird cannot go back on the ground. Here's a small program that uses our Bird:
bird = Bird.new

bird.eat('worm') # Ate worm!

egg = bird.lay_egg # Returns an Egg
egg.hatch! # Returns a new Bird

bird.fly! # @flying == true
Remember, any subtypes from Bird should be able to work in our program above. Now, let's create some subtypes of Bird and see how we can apply the Liskov Substitution Principle.

  • The subtype must implement the base type's interface.

In most programming languages, we can achieve this through basic inheritance. Since we already have a base class defined, we'll take this approach. However, there are many ways to achieve this across many languages. In Ruby, we can use modules to share methods (see duck-typing). In Java, we can implement interfaces.

Let's create a Pigeon subclass:
class Pigeon < Bird
end

bird = Pigeon.new # Behaves exactly like Bird!
Success! Pigeon now implements Bird's interface.

  • The subtype should not strengthen preconditions of the base type.

Let's say our Pigeons can only eat bread. We will override the eat method to achieve this:
class Pigeon < Bird
  def eat(food)
    if ['bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

# bird is now Pigeon
bird.eat('worm') # raises an error: "Does not eat worm!"
Since we've actually made the preconditions to our method stricter than in the Bird class, we've violated the Liskov Substitution Principle! In doing so, we've broken our existing program!

Instead, let's say that Pigeons can eat bread in addition to seeds and worms. Then, we've weakened the preconditions and are well within our rule:
class Pigeon < Bird
  def eat(food)
    if ['worm', 'seed', 'bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

bird.eat('worm') # "Ate worm!"
And our program works with our subclass!

  • The subtype should not weaken postconditions of the base type.

Let's say our Pigeon is some kind of mutant and doesn't actually lay eggs. We'll call it a MutantPigeon. Instead, no egg comes out at all:
class MutantPigeon < Bird
  def lay_egg
    nil
  end
end

bird = MutantPigeon.new

egg = bird.lay_egg # returns nil
egg.hatch! # raises an error: undefined method 'hatch!' for nil:NilClass
We've broken our program yet again! Since we've actually made the postconditions in our method less restrictive than in the Bird class, we've violated the Liskov Substitution Principle.

Instead, let's say that MutantPigeons actually return a more specific type of Egg. We'll call it MutantPigeonEgg, and it behaves just like Egg with a hatch! method. Then, we've strengthened the postconditions and are well within our rule:
class MutantPigeon < Bird
  def lay_egg
    MutantPigeonEgg.new
  end
end

egg = bird.lay_egg # returns nil
egg.hatch! # Returns a new MutantPigeon
And our program is happy again!

  • The subtype should preserve invariants of the base type.

Let's model a different bird this time. What about Penguins? As many people know, most penguins in the real world don't actually fly. So, we'll override the fly method with a no-op:
class Penguin < Bird
  def fly
    # no-op, do nothing
  end
end

bird = Penguin.new

bird.fly! # @flying != true
Looks like another break in our program! By doing nothing in our new fly method, we've broken the guarantee that the state of our @flying variable would be "true". Again, we've violated the Liskov Substitution Principle.

Now, this introduces an interesting problem. Penguins cannot just be made to fly, right?!

Real-Life Relationships != Inheritance-Model Relationships

Objects in the real world may show an obvious inheritance relationship. However, in object-oriented design, we only care about inheritance relationships regarding object behavior. Think of the classes in our system as representations of real-world objects. Those representations are fully defined by their external behavior (or interface).

Sure, penguins are birds in the real world, but Penguins are not Birds in our system because they do not behave like Birds. They don't have a properly functioning fly method.

Liskov Substitution and the Open/Closed Principle

Consider the examples above. Suppose we actually violated the Liskov Substitution Principle by creating our Pigeon class with a more restrictive eat method? Our existing program would have to be modified to handle our new class:
class Pigeon < Bird
  def eat(food)
    if ['bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

if bird.instance_of?(Pigeon)
  bird.eat('bread') # "Ate bread!"
else
  bird.eat('worm') # "Ate worm!"
end
As we know from the Open/Closed Principle, we shouldn't have to change existing code to add new requirements or features. By violating the Liskov Substitution Principle, we are forced to violate the Open/Closed Principle!

Conclusion

As with all programming principles, it's important to find a balance when applying the Liskov Substitution Principle in real-world scenarios. There is some debate over the benefits or detriments of the principle. Always keep it simple first, then refactor as needed.

Happy coding!

Tuesday, February 17, 2015

Share Your Local Web Server Publicly using ngrok

As a web developer, my workflow often includes writing a little code, firing up a local web server, and doing a quick smoke test to make sure things look alright. Occasionally, I'll encounter a situation where I'll need someone else (coworker, client, etc.) to provide some feedback on a design decision before moving forward. If I'm in the office, it's easy. I'll just call them over to take a look.

However, there are times when I need to share my local changes with someone remotely and get feedback quickly. If the change is relatively minor, deploying to some staging environment might be overkill. How can I expose my local web server publicly so my clients can view my changes and provide immediate feedback?

Enter, ngrok!


ngrok, creates a secure tunnel from a randomly-assigned, public internet address to a locally running web service. It also captures any traffic moving through the tunnel, allowing users to inspect HTTP request data. ngrok has several uses, and publicly exposing a local web server is one of them.

Installation


There really is no "installation" required. You simply visit the download page and follow the instructions. Since I'm an Ubuntu user, I downloaded the Linux .zip file, unzipped the binary, and began using it. Easy as pie!

Running a Web Server

Let's expose a local Rails web server! We'll assume a Rails project already exists and is ready to start. So, lets fire it up:
$ rails server

=> Booting WEBrick
=> Rails 4.0.4 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Now, if we go to http://localhost:3000, we should see something resembling the following:


Sharing Our Local Web Server

To expose our local web server to anyone over the internet, we simply navigate to our ngrok binary and run the following:
$ ./ngrok 3000
The "3000" argument is the local port of our web server. If you are hosting something other than Rails, it may launch on a different port. In this case, simply replace "3000" with the port number of your web server.

Once ngrok is launched, it will display the following:
ngrok (Ctrl+C to quit)

Tunnel Status                 online
Version                       1.7/1.7
Forwarding                    http://2779ffc7.ngrok.com -> 127.0.0.1:3000
Forwarding                    https://2779ffc7.ngrok.com -> 127.0.0.1:3000
Web Interface                 127.0.0.1:4040
# Conn                        0
Avg Conn Time                 0.00ms
Using the information provided above, we have our public web address: http://2779ffc7.ngrok.com. We can share this URL with our collaborators! Any visits to this URL will be tunneled to our local web server. As people visit the URL, ngrok will update by displaying any HTTP requests flowing through the tunnel:
HTTP Requests
-------------

GET /favicon.ico              200 OK
GET /                         200 OK

Further Request Inspection with the Dashboard

ngrok provides a introspection dashboard which is hosted locally at http://localhost:4040/.


Since all HTTP traffic is captured by ngrok, more detailed information about each request can be explored on the dashboard. The dashboard updates in real-time. So, as people view your exposed website, the dashboard will update along with each request. You can explore request time, duration, headers, parameters, etc. It even pretty-prints JSON and XML responses to make them easier to read.

Conclusion

As more companies allow remote-working among their employees, collaborative tools utilizing the web will become more important. In the fast-paced world of web development, gathering feedback quickly is much easier because of tools like ngrok!

Go on, give it a try! Let me know your thoughts by leaving a comment.