Object Oriented Design. Important Things

actor-model, dependency-injection, dependency-inversion, design, oop, ruby, tdd

Slides from my talk @brainly 12 Mar 2015

Disclaimer: This is my personal vision, based on my knowledge and experience. If you want to challenge it, ask questions, provide feedback and discuss, feel free to ping me on twitter: @waterlink000, I would love to hear from you.

Code examples are in ruby/pseudo-code.

Object Oriented Programming

Most notable features of OOP:

  • Encapsulation - keep data together with behavior that needs that data, effectively hiding this data from everything else.
  • Inheritance - usually a subclassing, inheriting all behavior and data, in some language even all private details.
  • Polymorphism - ability to substitute instances of one class with instances of others.

How important these features are for OO design?

Imagine, that given 100 points you want to distribute them between these 3 features, and each number will represent an importance of corresponding feature.

This would be my answer (and my personal opinion):

Encapsulation Polymorphism Inheritance
80 40 -20

And 100 = 80 + 40 + (-20) :)

Why inheritance is so bad?

Because it increases coupling (usually): subclass (usually) depends on some data and/or behavior of its superclass. Even worse: (usually) it is not public data/behavior. I.e.: it violates principle of encapsulation badly. (Usually).

There are actually cases, when you do want your classes to be in inheritance hierarchy, it is the case, when your domain has the same hierarchy naturally in real world. And type inheritance doesn’t really mean behavior inheritance.

So how to avoid violation of encapsulation using inheritance?

  • Be careful, use only public interfaces of your superclass.
  • Replace inheritance with composition and delegation, because when you use only public interfaces of superclass, then you don’t really need inheritance.

Coupling

Why coupling is bad?

Imagine you need to change behavior of class X.

You will have to change any other piece of code base, that directly depends on this behavior of class X.

More coupling you have, more changes will have to take place, and probably, in totally unrelated parts of codebase.

As a result it:

  • Exponentially increases time required for change
  • Invites bugs (lots of)

Dependency

Dependency, is basically what coupling is, - is not really your friend, so you need to watch out for them.

How to deal with dependencies?

  • Dependency inversion principle - Instead of referring foreign system/package/class/module/whatever, refer an abstract interface.
  • Dependency injection - Technique, that allows to provision all dependencies to parts of your system and basically fulfill all required interfaces.

An example

1
2
3
4
5
class Answer
  def rating
    RatingService.new.rating_for(comments, upvotes, downvotes)
  end
end

What is the problem with this code? - It is a dependency Answer -> RatingService. What if you wanted to A/B test different rating models? You will definitely have troubles with that approach, especially if it is not the only place, that reference RatingService.

Use dependency injection!

1
2
3
4
5
6
7
8
9
class Answer
  def initialize(rating_service)
    @rating_service = rating_service
  end

  def rating
    @rating_service.rating_for(comments, upvotes, downvotes)
  end
end

Now you can easily have multiple rating services and A/B test them, or do whatever you want, it is really flexible.

It is still coupling

But coupling to abstract interface, instead of real implementation, which means, you can exchange different implementations without changing users of this interface. Which basically improves polymorphism features of your code. It is very loose coupling.

Test Driven Development

How is it related to OO design?

It provides very short feedback on your OO design:

  • If you have troubles writing test - your design is wrong and you need to step back
  • If you don’t like how your test look like - your design is wrong and you need to step back
  • If you have troubles making test green - your design is wrong and you need to step back

It is really almost like pairing partner if used right! Of course pairing still provides even better feedback loop - real-time continuous feedback loop!

Unit tests vs integration tests

Integration tests are scam!:

  • Very slow => bad feedback loop
  • Exponential count of paths to test

Unit tests just don’t work. Are they?

Everything works in isolation != the whole system works as expected.

Testing in isolation = providing fake objects and/or mocks for all your dependencies

Mocks and fake objects can make your unit test green, but in fact the code is broken, because one of the dependencies has changed its behavior or even public interface in unexpected fashion.

Answer: Cut your system at value boundaries!

Instead of method call boundaries. And we arrive at Actor Model.

Actor Model

Given this example:

1
2
3
4
5
class RatingService
  def rating_for(comments, upvotes, downvotes)
    # .. calculate rating somehow ..
  end
end

Problems with this class:

  • Any user of this class will have to stub out its #rating_for in unit tests.
  • It invites additional behavior to be added (since it is as simple as adding additional public method), which will kill single responsibility feature of this class.

Actor Model solves this

Warning, it is ruby pseudo-code:

1
2
3
4
5
6
7
8
actor RatingService
  comments, upvotes, downvotes, outbox = inbox.fetch
  # .. calculate rating somehow ..
  outbox.send(rating)
end

rating_service = RatingService.new
rating_service.start

Way better now:

  • Allows unit testing easily without mocks and fake objects: by peeking inside its inbox in unit tests instead, and checking that it received right message.
  • It is really hard to pack more responsibility to this, since it has no methods, it has just one body, that is responsible for processing exactly one message.

You of course can encode something strange in message, and organize your own method dispatch mechanism through actors inbox, but that is just silly (usually).

Easy to unit test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# creating actor, but not starting it
rating_actor = RatingCalculator.new

# dependency injection of rating actor
answer_actor = Answer.new(answer_id, rating_actor)
answer_actor.start

render_actor = Render.new(answer_actor)
render_actor.run
expect(rating_actor.inbox)
  .to include([comments, upvotes, downvotes, outbox])

# fake response from rating actor
outbox.send(3.5)
expect(render_actor).to render_rating(3.5)

In unit tests you start only one actor - actor under test, and all other actors just get instantiated correctly and passed in as a dependencies where needed. Since they are not started, they will not consume any messages from their inbox, which means that you can consume these inboxes from your unit test, and check that the messages that arrived at inboxes are expected.

When the code is done

  • It works!
  • It is readable (future me will not curse me for writing this code)
  • It has no duplication
  • And it is as short as possible (while maintaining all of the above)

Code comments

Basically a code smell (I’m not talking about documentation comments)

Example:

1
2
3
# when user is active
if activity_service.has_events(user.id, min_date: 2.weeks.ago)
  && !user.fraud?

Which is bad from more than one point of view, it literally should have been:

1
2
3
4
5
if user.active?
# or
if activity_service.user_is_active?(user)
# .. more variations can be here ..
# .. but all of them will be better ..

If code needs comment:

  • it is not readable
  • it fails to communicate its intent

You should be able to read the code and understand it. In that order: read -> understand.

You shouldn’t interpret it in your head. You shouldn’t have Ruby (or your favorite language here) instance running in your head.

To sum it up

  • Inheritance is good only in very rare cases
  • Coupling and dependencies are not your friends, take them under control with dependency inversion & injection
  • TDD as a shortest feedback cycle for your OO design
  • Write code in such way, that you would thank yourself for that in the future

Recommended reading: “Pragmatic Programmer: From Journeyman to Master” by Andrew Hunt and David Thomas. It is insanely good, concise book with lots of awesome references to other resources.

Thanks!

If you have any questions or suggestions, you can always reach me out on twitter @waterlink000.

How Can contracts.ruby Be Used in the Community With Duck Typing Culture?

contracts.ruby, design-by-contract, ruby

So, given simple example:

1
2
3
4
Contract Num, Num => Num
def add(a, b)
  a + b
end

One can ask: “But it is ruby, what about duck typing, I want just pass two things that have certain methods defined on them”

And my answer, you can easily do that:

1
2
3
4
5
Contract RespondTo[:save, :has_valid?], RespondTo[:to_s] => Any
def assign_user_a_default_email(user, default_email)
  user.email = default_email unless user.has_valid?(:email)
  user.save
end

This is a built-in RespondTo contract. You can get a list of all of them here: http://egonschiele.github.io/contracts.ruby/#built-in-contracts

If you have any questions, you can always ping me at twitter @waterlink000

Introduction to contracts.ruby

design by contract, ruby

Slides from my talk on RUG-B Mar 2015

A short introduction to a powerful Design by Contract technique and its implementation in ruby contracts.ruby.

Design by Contract allows one to do defensive programming in very elegant fashion, allows to set contracts on methods (expectations on input - arguments; and on output - return result) and invariants on classes. This allows to reason about code much much better.

Classical defensive programming

Lets start from simple code example:

1
2
3
def add(a, b)
  a + b
end

If you want to be really confident in implementation and usage of this method, you would probably use something like that:

1
2
3
4
5
6
7
8
9
10
def add(a, b)
  raise "a should be Fixnum or Float" unless a.is_a?(Fixnum) ||
    a.is_a?(Float)
  raise "b should be Fixnum or Float" unless b.is_a?(Fixnum) ||
    b.is_a?(Float)
  result = a + b
  raise "result should be Fixnum or Float" unless result.is_a?(Fixnum) ||
    result.is_a?(Float)
  result
end

Which definitely provides guarantees for input and output values.

But this code is extremely ugly, unmaintainable and unreadable. You can always extract assert-like helper methods, but it will not improve readability too much, you want to have just this simple a + b in the body of this method.

gem "contracts"

1
2
3
4
Contract Num, Num => Num
def add(a, b)
  a + b
end

This code does the same thing, but readability at a totally different level. Developers who know haskell may find this notation quite familiar.

Design by contract

When applying design by contract technique to development of any system or service, it allows you to answer the following questions:

  • What does it expect? - Restrictions on input data for the system.
  • What does it guarantee? - Restrictions on output data (return value) of the system.
  • What does it maintain? - Restrictions on the inner state of the system (if your system is stateful, of course).

Benefits

Benefits of being able to answer this questions and enforce them on a runtime level are:

  • Clients of your system can be confident using its public APIs. They can be sure, that if they provide something wrong, then they will get a convenient error immediately. And they can be sure, that system returns the right value as a result.
  • System or service itself can be confident in its own operations. Implementation of system, that is covered with contracts, can assume that all the data flowing through the system is right and expected, and don’t waste time (and lines of code, and sanity of the developer/maintainer) on different checks, conversions and so on (ie on defensive programming), it can just do what it needs to do, in confident, concise and convenient way, right up to the point.

assert on steroids. And it is not only about types

Up until now it may seem like some kind of runtime type-checking system. But it is not, it is way more powerful.

You can check for exact value:

1
Contract 200, nil, :get => "ok"

You can check for types:

1
Contract User, Time => Or[TrueClass, FalseClass]

You can check for anything that is available to you at runtime:

1
2
3
4
5
6
7
8
9
10
Contract ActiveUser => Rating
def rating_for(active_user)
  # .. calculate rating for active user ..
end

class ActiveUser
  def self.valid?(user)
    user.last_activity > 2.weeks.ago
  end
end

As you expect when contract check on active_user argument happens, it will just call ActiveUser.valid?(active_user) and in case of falsy result will raise contract violation error.

Very useful contract violation errors

1
2
3
4
5
6
7
ContractError: Contract violation for argument 1 of 1:
    Expected: ActiveUser,
    Actual: #<User:0x00000101059540> {last_activity=27.11.2014}
    Value guarded in: Object::rating_for
    With Contract: ActiveUser => Rating
    At: (irb):10
    ... backtrace ...

This kind of errors tell you, what exactly you did wrong and where exactly you did it wrong. It is totally different from usual NoMethodError :something for nil:NilClass, because usually these kind of no-method errors can occur in totally different part of codebase comparing to where these errors actually were introduced. Contract violation will be issued exactly at the place where you passed invalid data into or out from your system. So that when you see a contract violation error, there is a high chance that you already know how to fix it.

Pattern matching, sorta..

You can say even method overloading. Very simple example:

1
2
3
4
5
6
7
8
# factorial in classic way
def factorial(n)
  if n == 1
    1
  else
    n * factorial(n - 1)
  end
end
1
2
3
4
5
6
7
8
9
10
# factorial using pattern matching
Contract 1 => 1
def factorial(_)
  1
end

Contract Num => Num
def factorial(number)
  number * factorial(number - 1)
end

When I saw this example, my first reaction was: “Wow!”. I was very excited about this feature.

Something useful with pattern matching

Last example was not particularly useful for our everyday development, but here you go.

Imagine you have a concurrent evented system, that needs to make asynchronous requests to some external http service(s). You may eventually end up with handler functions like these:

1
2
3
4
5
6
7
8
# Classical way
def handle_response(status, response)
  if status == 200
    transform_response(JSON.parse(response))
  else
    wrap_in_error(status, response)
  end
end
1
2
3
4
5
6
7
8
9
10
# And using pattern matching:
Contract 200, JsonString => JsonString
def handle_response(status, response)
  transform_response(JSON.parse(response))
end

Contract Fixnum, String => JsonString
def handle_response(status, response)
  wrap_in_error(status, response)
end

Limitless benefits

  • All your input data is consistent
  • All data flows inside of your system are consistent
  • State of your system is consistent
  • Output of your system is consistent (or it is a contract violation error)
  • Blows up loudly on any logical error in your system

Last point is extremely important, because sometimes logical errors in classical programs will not lead to any failure at all, they will just do the wrong thing. For example, transfer money to wrong bank account. In such mission critical systems it is really important to fail fast to not allow error to propagate throughout your system.

Caveats: Performance

Benchmark Slowdown
a+b 900% slowdown
production system with network IO 5-10% slowdown
NO_CONTRACTS=1 0% slowdown

First benchmark is simple comparision of a + b with and without contract. Since a + b itself is very fast, then the slowdown is huge. But if you try to benchmark any real world system, that actually does something useful (communicates to other services through network for example), then slowdown is very very small.

And you have ability to disable contracts in production with NO_CONTRACTS=1 environment variable. But beware, you lose extremely important benefit of blowing up on logical error immediately before letting error propagate. This benefit itself outweights these 5-10%, at least for me.

Useful links

If you have any questions or suggestions, you can always reach me out on twitter @waterlink000. If you have any issues with using contracts.ruby, you can always create an issue on github and Pull Requests are welcome.

Dismantling Effective Go Article

go

Go is a nice language and giving tips on how to better write code in that language right away at first language tutorials is awesome.

Article itself: https://golang.org/doc/effective_go.html

And here is a list of my thoughts.

Formatting

I generally like this approach, it is nice to have such feature in your language tools available right there and with already existing integrations to popular editors.

Except one thing:

Parentheses

Go needs fewer parentheses than C and Java: control structures (if, for, switch) do not have parentheses in their syntax. Also, the operator precedence hierarchy is shorter and clearer, so

x<<8 + y<<16

So we kill parentheses and replace them with whitespace. I am not entirely sure it is better. How will this work then?

1
x << 8 + y << 16

And what about this?

1
x << 8    +    y  <<  16

Probably nobody will write it like that, but one space miss, and lots of minutes lost in debugging.

Semicolons

Oh, lexer inserting semicolons where it pleases? What can go wrong. Reminds me about javascript and its problems when you omit semicolons.

And this example looked strange to me:

One consequence of the semicolon insertion rules is that you cannot put the opening brace of a control structure (if, for, switch, or select) on the next line. If you do, a semicolon will be inserted before the brace, which could cause unwanted effects. Write them like this

 if i < f() {
     g()
 }

not like this

 if i < f()  // wrong!
 {           // wrong!
      g()
 }

First I thought that it will accept this input, put a semicolon after if, compile successfully and always execute statement inside of block, especially because it said “could cause unwanted effects”. But turned out this code results in compilation error - so no problem here, but probably this should have been clarified in the article.

Control Structure

Mandatory block - looks good to me.

But the last example worries me:

This is an example of a common situation where code must guard against a sequence of error conditions. The code reads well if the successful flow of control runs down the page, eliminating error cases as they arise. Since error cases tend to end in return statements, the resulting code needs no else statements.

f, err := os.Open(name)
if err != nil {
     return err
}
d, err := f.Stat()
if err != nil {
     f.Close()
     return err
}
codeUsing(f, d)

That code is definitely not narrative, I will put some comments with kind of operation that is done:

1
2
3
4
5
6
7
8
9
10
f, err := os.Open(name)   // get input
if err != nil {           // check errors, guard
    return err
}
d, err := f.Stat()        // get additional input
if err != nil {           // check for errors, again, guard
    f.Close()             // cleanups
    return err
}
codeUsing(f, d)           // do something

This way it is hard to understand and reason about.

Why not just:

1
2
3
4
5
6
f = os.Open(name)         // get input
d = f.Stat()              // get input
result = codeUsing(f, d)  // do something
if result.IsErorr() {     // handle errors
    f.Close()             // cleanup
}

Of course f, d and result are just Result/Either monads, they can be either in normal or faulty state. Whenever you try to do anything on Result in faulty state, it will just return itself. But Result in normal state will proxy call to its contents and wrap it in another Result monad (normal or faulty - depends on if call was successful or not). Probably I am too critical about that, because here I used some patterns that are not part of the language itself, but yeah, I can’t look at code that mixes guards and actions.

Probably, somebody out there in go-lang world can tell me, is it common to use patterns like Maybe, Result, NullObject and so on in go-lang? Or everybody just go with simple code without any magic behind the scenes, and just do it like in the article’s example? Feel free to ping me at twitter @waterlink000.

For

Lets move on..

For strings, the range does more work for you, breaking out individual Unicode code points by parsing the UTF-8. Erroneous encodings consume one byte and produce the replacement rune U+FFFD. (The name (with associated builtin type) rune is Go terminology for a single Unicode code point. See the language specification for details.)

I really like that a term for this “unicode minimal entity” was invented. And after some while the name actually makes a lot of sense. And it sounds good.

Switch

That surprised me. Feels like assembler:

Although they are not nearly as common in Go as some other C-like languages, break statements can be used to terminate a switch early. Sometimes, though, it’s necessary to break out of a surrounding loop, not the switch, and in Go that can be accomplished by putting a label on the loop and “breaking” to that label. This example shows both uses.

Loop:
    for n := 0; n < len(src); n += size {
        switch {
            case src[n] < sizeOne:
                if validateOnly {
                    break
                }
                size = 1
                update(src[n])

            case src[n] < sizeTwo:
                if n+1 >= len(src) {
                    err = errShortInput
                    break Loop
                }
                if validateOnly {
                    break
                }
                size = 2
                update(src[n] + src[n+1]<<shift)
            }
        }

Not exactly the original assembler label, one only use it to mark (aka tag) the loop itself and use it to break out of it. Interesting concept, but something clicks in my head when I look at this.

Type switch

Good one, “Switching on Type”, (usually) a code smell, builtin into language. But still it has its own uses when used carefully.

Multiple return values

Why not tuples? If you just introduce tuples and destructuring, then you don’t need multiple return values, only one return value - tuple itself.

Defer

This is a nice one. Allows to simplify previous example with files even more:

1
2
3
4
f = os.Open(name)         // get input
defer f.Close()           // deferred cleanup right after acquiring of `f`
d = f.Stat()              // get input
result = codeUsing(f, d)  // do something

Particularly interesting example with trace/untrace follows in the article.


To be continued…

RSpec Json Expectations - Set of Matchers and Helpers to Allow You Test Your API Responses Like a Pro

api, json, rspec, ruby

rspec-json_expectations library provides powerful include_json matcher for your RSpec suites. It allows to match string with JSON or already parsed ruby Hash against other ruby Hash, which is very convenient and creates very readable spec code. Lets jump to some examples.

It can handle some plain json:

1
2
3
4
5
6
7
it "has basic info about user" do
  expect(response).to include_json(
    id: 25,
    email: "john.smith@example.com",
    name: "John"
  )
end

And nested json:

1
2
3
4
5
6
7
8
9
it "has gamification info for user" do
  expect(response).to include_json(
    code: "7wxMw32",
    gamification: {
      rating: 93,
      score: 355
    }
  )
end

You can even do some regex matching:

1
2
3
4
5
6
it "has basic info about user" do
  expect(response).to include_json(
    code: /^[a-z0-9]{10}$/,
    url: %r{api/v5/users/[a-z0-9]{10}.json}
  )
end

Most can agree, that this method of specifying JSON responses in ruby is very readable, but what about failure messages? How helpful they are?

For example with failure in nested JSON things can become tricky, but this gem solves them quite nice:

1
2
3
4
             json atom at path "gamification/score" is not equal to expected value:

               expected: 355
                    got: 397

If you match with nested Arrays you will get numbers in your JSON path within failure message, for example:

1
2
3
4
5
6
             json atom at path "results/2/badges/0" is not equal to expected value:

               expected: "first flight"
                    got: "day & night"

             json atom at path "results/3" is missing

For further reading and instructions: github and cucumber generated documentation.

Feedback is highly appreciated, contact me on twitter (@waterlink000) or on github issues.