Ruby and Human Compilers

Posted by Paul Pagel Tue, 26 Sep 2006 18:38:00 GMT

In Paul Graham’s Book, Hackers and Painters, he talks about patterns and Human Compilers. I started to think about how some traditional Object-Oriented patterns are implemented in different languages. What problems do these patterns solve?


Paul Graham’s human compiler is an interesting concept I would like to explore more with relation to ruby, which is what I have been working in lately. The object-oriented design patterns I have been taught are intended for one reason, to increase productivity. These patterns produce well decoupled, easily testable, clean, and reused code that makes it easier for developers to extend and maintain an application. It is the mind of the developer that produces this compiler to be able to read and interpret these patterns to perform workflow optimizations. When I see a pattern in the code, I can easily navigate the implementation and the problem that it is solving. I can hold the entire context in my mind to work on that piece of code. This human compiler is what allows me to move quickly through an unfamiliar piece of code. Moving to dynamic languages, I see the context I need shrinking in the traditional way, holding onto patterns. So I started to wonder, could we build patterns into the language better, or eliminate the need for them? Are the GOF patterns mostly just a method of satisfying our human compiler for statically typed languages?


I have suspicions that moving to ruby from C++/C#/Java will eliminate the need for many of the design patterns we have coveted in those languages. I would like to explore a few of them to prove this thesis. I want to look at the patterns that have helped make testing/design/decoupling better and see how they are different in ruby. Also, how can we transfer the values behind those patterns? The patterns were all based on good decisions, and can we learn anything from them or should we throw away the thought process that made them necessary?

Lets start by taking the most commonly used pattern, Abstract Server. Writing in C#, I need to create interfaces to make sure I don’t violate the Interface –Segregation Principle and depend on concrete classes. This becomes a big deal in a statically typed language because you don’t want to have to depend on a specific type. However, in ruby, you respond to messages of a class, which don’t care what type the return values are. Also, because it is a compiled language, you don’t want to have to recompile the clients of a class just because the implementation changes. In fact, in ruby, the Interface-Segregation violation goes away since there is no need for explicit interfaces to abstract, rather every class becomes an interface for itself.


Have you ever been working on a java/C++/ C# project and noticed an inheritance hierarchy that makes you twinge? Statically typed languages abuse inheritance by definition. Why do you need interfaces? Shouldn’t an abstraction be one of behavior? In ruby, you send messages to objects, which is the same type of firewall, as each object acts as its own interface. However, since an inheritance tree got there, you need to decouple it since it is growing large and not maintainable. Or have you seen interfaces placed in front of a class for the sole value of abstraction. A good example of this I have seen is with Views in the Model/View/Presenter pattern. Something that looked like this:

public interface ISomeView
{}

public class SomeView : ISomeView
{//some implementation}



This uses the philosophy that the Views need to be decoupled from the Presenters. This is true, but the point of decoupled is missed. Decoupling is a method of using firewalls to hide the implementation of code from its clients (Abstract Server). How does this degenerate do this? It doesn’t. I have seen projects where inheritance is used liberally and there are interfaces for everything. I have also seen projects where inheritance is used conservatively and composition is used for everything, to prevent the formation of highly coupled inheritance trees. Finding the proper balance in static languages is difficult, but it is also a problem that should not exist. In ruby, inheritance is used on a YAGNI basis, as you only use it in the clearest of cases. It makes inheritance a tool to be used rather than monitored for abuses.

In ruby, you only inherit through necessity of behavior rather than clarity for form. Inheritance in most static languages is used as a tool for design patterns to make the code easier to read/work on/maintain for the developers. This is unnecessary in ruby since it is built into the language. Ruby does it instead of your human compiler. Writing beautiful code in java meant making the code clean and decoupled. Inheritance is no longer needed to do that in ruby, as it only serves a utilitarian purpose. Form is just as important in ruby as it is in static languages, it is just expressed without using features meant to serve efficiency purposes.

Self Shunt 2

Posted by Paul Pagel Mon, 11 Sep 2006 16:06:00 GMT

The self-shunt method of testing has been conflicting for me. Self-shunt is a good testing pattern to test observers/views. A colleague of mine has started to convince me that self-shunt has some serious drawbacks. I want to explore the different ways to address the same pattern and how they fit into the test process and the test content. Are certain patterns easier to write and certain ones have a better final form?

Lets look at some code from Michael Feathers self shunt paper (see bottom for link). I extended it to do the mocking examples. The user story we are working with is the name of an item must be displayed on the led when the item is scanned.

Writing all of the tests did not require a concrete window (Display). We didn’t need to create the stub of the file to use; we could just use the interface. All the test patterns let the test be isolated in the test class/interface/production class triad. As a developer, it is easier for me to produce faster without quality loss if my context of thought is limited as much to the scope of the test as possible. Each extra window I switch is more contexts to deal with.

Here are three different tests:

public class ScannerTest extends TestCase implements Display
{
    private Item lastItem;

    public void testScan()
    {
        Item item = new Item("corn flakes");
        Scanner scanner = new Scanner(this);
        scanner.scan(item);
        assertEquals(item,lastItem);
    }

    public void displayItem(Item item)
    {
        lastItem = item;
    }
}

The first test is making user of self-shunt test. Self-shunt was a quick way to start writing a test. The implementation on the interface in the test class will give you a good idea on what the implementation of the concrete class should look like when you get there. Also, the test is simple and the syntax is expressive. However, the scalability is questionable. If you had more than one self-shunted interface in a single test file, it would be hard to start differentiating between which variables are used in which. Also, moving around the file to get a good sense of what a test is doing is misdirectional and distracting. Ideally, I want to see the entire test on one screen, so I can read it. If there were many implemented methods in a test file that were not tests or helpers, there is the potential to be confused.

public class ScannerTest   extends TestCase
{
    public void testScan()
    {
        Item item = new Item("corn flakes");
        Display mockDisplay = new MockDisplay();
        Scanner scanner = new Scanner(mockDisplay);
        scanner.scan(item);
        assertEquals(item,((MockDisplay)mockDisplay).lastItem);
    }
}

public class MockDisplay implements Display
{
    public Item lastItem;
    public void displayItem(Item item)
    {
        lastItem = item;
    }
}

Then I wrote the hand mock test. This test got rid of the clutter of implementing interfaces in the test and having local variables. Also, there are no external libraries needed, like a dynamic mocking library. The test is simple and easy to read. The biggest drawback is dealing with the hand mock test file. It is untested code that I have seen a tendency for behavior to slip into, which is disastrous. It can cause a bug in your tests that take too long to track down. Ideally this doesn’t happen, but I have seen it many times.

public class ScannerTest extends TestCase
{
   public void testScan()
   {
       MockControl control = MockControl.createControl(Display.class);
       Display mockDisplay = (Display) control.getMock();
       Item item = new Item("");
       mockDisplay.displayItem(item);
       control.replay();
       Scanner scanner = new Scanner(mockDisplay);
       scanner.scan(item);
       control.verify();
   }
}

Finally the dynamic mock test was written. This test was the simplest to write as a matter of process also the fewest lines of code. I never needed to leave the scope of the test function. It was just setting up my expectations and letting the test run. As Micah Martin pointed out to me, the test execution seems backwards and unreadable with dynamic mocks. The verification statements are at the beginning of the method and the execution is at the end. This is different than all the other tests in the system that follows an intuitive flow cycle of build, operate, and check.

With a little investigation I found Michael Feathers paper on Self Shunt (www.objectmentor.com/resources/articles/SelfShunPtrn.pdf).

Sentential Testing

Posted by Paul Pagel Sun, 06 Aug 2006 04:51:00 GMT

In a project I have been working on, we recently decided to allow wrapped assertions to customize our unit tests. Our purpose was to make the unit tests be sentential enough to explain the meaning of the assertion in the execution of the assertion.

We can’t get rid of all developer comments (variable and method names) because, if we did, we would lose the ability to easily read and, consequently, easily maintain the code. In fact, writing easily readable code is a primary goal of good practices, since it is directly correlated with making code easily maintainable. This is important because maintenance is the most expensive part of the development process. However, we must be cautious about writting comments, since they can often have the opposite effect‚ less maintainable code‚ when they are misrepresentations or plain lies. There is a balance that results in easily read and understood tests. Investing in writing tests to tell a story to the developers who may have to look at the tests in the future, makes maintenance and TDD easier and with a better flow.

We chose to achieve this balance by merging the assertions with the comments of the execution of the test, resulting in a pseudo-sentence. The statement is an explanation for the next developer to read. The form we chose is

Verify.That(positiveAssertionString).ProvedBy(subject).verb(predicate);

An example of one of these assertions is

public void test_PressingBrakesShouldDecelerateVehicle()
{

 int originalSpeed = vehicle.currentSpeed;
 vehicle.brake();

 Verify.That(‚"Brakes decelerate vehicle.").ProvedBy(vehicle.currentSpeed).IsLessThan(originalSpeed);<br>

}

When a developer comes across this story, along with PressingEmergencyBrakeLocksTires(), SlamOnBrakesShouldSkid(), and BrakesShouldAntiLockAtIntervals(), the developer is told a story of what the class is doing. Why do we use custom asserts when good test names will solve the problem?

Changing tests should be cheap. As new designs and behavior evolve in the system, tests are changed to conform to the new structures. When implementing a new design to an existing system (such as breaking up an inheritance in favor of composition), I change the tests to fit the new structure on a macro level. However, this often implies that I am changing the behavior (if only slightly) to please the new design. Yet, test names, like comments, are not executable, so you can‚’t trust them to tell you what the tests test. On a system with heavy refactorings, a test might have its details changed five times without the developer thinking to verify the integrity of the names. An answer is to write sentential explanation of the test as the assert statement since the assert statement executes. I will trust execution over comments every time.

There is a project in its early stages called NSpec(.NET), which is a framework for assertions of a more sentential form. The project is based on the idea of Behavior Driven Development (BDD). RSpec, the same project for ruby has a form of BDD assertions which take full advantage of the features of the dynamic language. It allows you to add the assertion to the object from the test.

For example:
def pressingbrakesshoulddeceleratevehicle
vehicle.brake
vehicle.decelerating?.should_be_true
end

This is really cool! It defines the test as behavior of the object, which it is. However, when the code isn’t ruby, there needs to be other means in place to bind the description of the assertion to something which executes.




Fri, 20 Jan 2006 05:37:12, nraynaud, Tests in a changing world You’re right, we are slowly going to agile world, and for now we have lots of meaningless test taken randomly from existing code (does it makes sense putting a multi megabytes source file in the compiler you test and expecting exactly 2008 errors? without even covering 10% of the compiler). We work on a language, yesterday, I added a restriction to the langugage, it took me 10 minutes to implement and test the change but the rest of the day to change all the others tests.

Moreover, I thank Intellij Idea that renames not only classes but also varialbes named after that class and even comments, this is on the way you takes.



Fri, 20 Jan 2006 13:13:22, David Chelimsky, should_be_decelerating I’ve just submitted a contribution to rspec that will make this even cooler. If vehicle can respond to any of these messages with a boolean value: vehicle.decelerating
vehicle.decelerating?
vehicle.is_decelerating
vehicle.is_decelerating?
then you can write this in your test:
def pressingbrakesshoulddeceleratevehicle
vehicle.brake
vehicle.should_be_decelerating
end
Admittedly, this may be confusing to a programmer who is looking for a “should_be_decelerating” method on vehicle, but the messaging is clear it fails (” should be true”) and it definitely improves the communication of intent. If you’re doing BDD (i.e. spec-first), you’ll get a !-NoMethodError-! on “decelerating”. Then it’s up to you to implement a method responding to any of the forms described above.

Fri, 20 Jan 2006 13:57:32, , How about using Higher Order Messaging style and let the user write:
vehicle.should_be.decelerating?
The test framework could mix should_be and similar higher-order messages into the Kernel module.

Fri, 20 Jan 2006 14:40:41, David Chelimsky, should_be That’s kind of how rspec works now. shouldequal, shouldcontain, shouldbe_true, etc are all mixed in to Object, so you can do things like someobject.someattribute.shouldbetrue. Your suggestion of tying it to a “shouldbe” method (rather than parsing the shouldbe_xxx message) might be more clear. I think, though, that “shouldbe.decelerating?” is weird to read (because of the question mark). Programatically, however, it does make the statement look more like the implementation. I’ll think on it some more…

Older posts: 1 ... 23 24 25 26 27 ... 30