473,546 Members | 2,308 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

LSP and programming by contract

11,448 Recognized Expert MVP
Greetings,

This tip of the week introduces a bit of theory when it comes to Object Oriented
programming. First we indroduce a little example project: a simple geometry
project. We want to define a bunch of classes that represent resizable shapes.
Nothing complicated much: we define an abstract Shape class and there we go:
Expand|Select|Wrap|Line Numbers
  1. public abstract class Shape { 
  2.    abstract public double getArea();
  3.    ... 
  4. }
  5. public class Rectangle extends Shape {
  6.    // it's dimensions
  7.    private double width, height;
  8.    // contructor
  9.    public Rectangle(double width, double height) {
  10.       this.width= width;
  11.       this.height= height;
  12.    }
  13.    // getters and setters:
  14.    public double getWidth() { return width; }
  15.    public double getHeight() { return height; }
  16.    public void setWidth(double width) { this.width= width; }
  17.    public void setHeight(double height) { this.height= height; }
  18.    // get the area
  19.    public double getArea() { return width*height; }
  20. }
The abstract shape class doesn't know how to calculate an area, thus the method
is made abstract. The Rectangle defines this method. Because a Rectangle *is-a*
Shape, we extend from the abstract class shape. This little scenario can be
found in almost any beginner's textbook; it's almost boring to look at this
for the umptiest time.

We finish this boring little project by defining a Square. As we all know a Square
*is-a* special type of Rectangle, so we extend the Rectangle class:
Expand|Select|Wrap|Line Numbers
  1. public class Square extends Rectangle {
  2.    // constructor
  3.    public Square(double side) { super(side, side); }
  4.    // getters and setters:
  5.    public double getSide() { return getWidth(); } // or getHeight()
  6.    public void setSide(double side) {
  7.       super.setWidth(side);
  8.       super.setHeight(side);
  9.    }
  10.    public void setWidth(double width) { this.setSide(side); }
  11.    public void setHeight(double width) { this.setSide(side); }
  12. }
So there: finished. We used most of the Rectangle code: we didn't even had to
define a new getArea() method and everything is fine. We wrap everything up,
build a .jar file, burn it on a CD (we even stick a nice label on it), send
the CD and the bill to our customer and we're going to enjoy the weekend.

That same day our customer receives the CD, installs everything and just
because he's a nasty nitpicker, he wants to test it:
Expand|Select|Wrap|Line Numbers
  1. public static void main(String[] args) {
  2.    Rectangle r= new Rectangle(1.0, 0.0);
  3.    for (int i= 1; i <= 10; i++) {
  4.       r.setHeight(i);
  5.       if (r.getArea() != i)
  6.          System.err.println("This software is not correct!);
  7.    }
  8. }
Later that day our customer gives us a call and tells us that the tests had
passed fine so far. We know our software is fine and we congratulate our
customer with his new, fine, correct software.

Our customer also knows that a Square *is-a* special type of Rectangle so he
crafts his next test:
Expand|Select|Wrap|Line Numbers
  1. public static void main(String[] args) {
  2.    Rectangle r= new Square(1.0);
  3.    for (int i= 1; i <= 10; i++) {
  4.       r.setHeight(i);
  5.       if (r.getArea() != i)
  6.          System.err.println("This software is not correct!);
  7.    }
  8. }
Guess what? Later that day our customer calls again: he's furious; nine out
of ten test runs failed; just the unit square test succeeded. All he did was
instantiating a Square instead of a Rectangle and because a Square *is-a*
Rectangle everything is supposed to work correctly but it doesn't.

We decide to skip that bill for the moment and have another look at our
classes; there goes the weekend.

Before midnight comes we realize that we're on the wrong track:
no matter what we do to keep the width and height have equal values, the
getArea() gives unexpected results if we consider our Square to be a special
kind of Rectangle. We can't do this either:
Expand|Select|Wrap|Line Numbers
  1. public class Square extends Rectangle {
  2.    ...
  3.    public void setWidth(double width) {
  4.       throw IllegalArgumentException("Don't use this method for Squares!");
  5.    }
  6.    public void setHeight(double height) {
  7.       throw IllegalArgumentException("Don't use this method for Squares!");
  8.    }
  9. }
This hack most certainly doesn't pass our customer's test suite. But a Square
*is-a* special kind of Rectangle. Or is it?

In 1988 Barabara Liskov made the following observation about this problem:

"What is wanted here is something like the following substitution property:
If for each object o2 of type D there is an object o1 of type B such that for
all programs P defined in terms of B, the behavior of P is unchanged when o2
is substituted for o1 then D can be a subtype of B."

- Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988).

Barabara Liskov was the mathematical kind of type and mathematicians are like
the Greek: you give them a problem, they translate it to their own language
and all of a sudden you don't understand your own problem anymore. (*)

For us regular folks who might wear tennis shoes or an occasional python boot,
she said this: the program P is our customer's test suite (see above). The o1
object is an object from the Rectangle class. Our customer's test suite works
fine with them (remember the first happy phone call?) If something wants to
be a derived class (object o2 from class D which in our example is a Square)
that same program should continue to work fine on it.

This is all fine and dandy but our Square class (which extends a Rectangle)
doesn't pass this substitution test. And that is what Barabara Liskov's LSP
is all about: if a derived class doesn't pass this test, it shouldn't be a
derived class at all.

But then again: a Square *is-a* Rectangle. In a mathematical sense yes it is,
but in our example where we can resize those things a Square isn't a Rectangle.
The *is-a* relation isn't a mathematical relation de facto. It is a behavioural
relationship and our attempts failed miserably in the LSP test drive.

That program P expects a Rectangle to operate on, no matter whether or not
they're "special" rectangles. If it runs fine on a rectangle it should run fine
of a special Rectangle. If it doesn't run fine, that special Rectangle is not
supposed to be a Rectangle at all and is not supposed to be a subclass of the
Rectangle class. That's what the LSP is all about. It's a sort of litmus test
for your inheritance tree: if a child class fails to behave as its parent:
don't make the class a child of that parent. (it normally doesn't work that
way in the real world ;-)

So finally, here's our redesigned Square class:
Expand|Select|Wrap|Line Numbers
  1. public class Square extends Shape {
  2.    private double side;
  3.    // constructor:
  4.    public Square(double side) { this.side= side; }
  5.    // getters and setters: 
  6.    public double getSide() { return side; }
  7.    public void setSide(double side) { this.side= side; }
  8.    // the area implementation:
  9.    public double getArea() { return side*side; }
  10. }
Boring isn't it? But now it passes the LSP!

There's one thing left: why did Barbara Liskov write "then it *can be* a
subclass". The answer for Java is simple: if two classes implement the same
interface and that program P expects that interface, it should run fine
no matter which of the different implementations (classes) of that same
interface are passed to that program P.

A bit after the LSP was formulated, Bertrand Meyers came up with the
"Design By Contract" notion, which borders the LSP. Every method has its
preconditions and postconditions. When a method is invoked, its precondition
must hold; when a method completes, it promises that its postcondition holds.

As an example, the postcondition of the setWidth method in the Rectangle
class, is:

this.width == width && this.heigth == oldHeight

where oldHeight represents the value of height before the method was called.
As Meyers wrote:

"when rewriting a method (in a derived class), you may only replace the
precondition by a weaker one and replace the postcondition by a stronger one
w.r.t. the conditions of the superclass method"
.

A "weaker" precondition is true if the "stronger" precondition in the superclass
is true. In other words, when using an object through its base type, the user
(the program P in LSP) knows only the preconditions and postconditions of
the base class. Thus, derived objects must not expect such users to obey to
preconditions that are stronger than those required by the base class. That is,
they must accept anything that the base class would accept. Also, derived
classes must conform to all postconditions of the base, i.e. their behaviour
and outputs (side effects) must not violate the constraints imposed by the base class.

As can be clearly seen, the Rectangle/Square example violates the ‘stronger
postcondition’ in the Square class, i.e. the postcondition shown above for the
Rectangle class doesn’t hold in the derived Square class.

'till next time which will be "playtime": Sudoku solvers and all that.

kind regards,

Jos
May 6 '07 #1
0 6031

Sign in to post your reply or Sign up for a free account.

Similar topics

90
4813
by: Bret Pehrson | last post by:
This message isn't spam or an advertisement or trolling. I'm considering farming some of my application development to offshore shops (I'm in the US). I have absolutely *no* experience w/ this, and therefore I'm looking for comments, suggestions, etc. on how to go about this w/o getting screwed. My current application development is...
6
1930
by: Ricky W. Hunt | last post by:
It's dawning on my a lot of my problems with VB.NET is I'm still approaching it in the same way I've programmed since the late 70's. I've always been very structured, flow-charted everything, used subroutines, etc. Now I'm trying to study this new way and I'm getting some terms confused and can find no clear definition (some even overlap or...
81
2746
by: Russ | last post by:
I just stumbled onto PEP 316: Programming by Contract for Python (http://www.python.org/dev/peps/pep-0316/). This would be a great addition to Python, but I see that it was submitted way back in 2003, and its status is "deferred." I did a quick search on comp.lang.python, but I don't seem to see much on it. Does anyone know what the real...
2
1681
by: DSpark | last post by:
I was a wondering whether anyone had developed and is will to share a database to monitor business contracts and services and monitor there performance. Ideally what Im looking for is a MS Access database to store Business Contract Information relating to who authorised it, start date, end date, Contract Amount and for reports to indicate when...
4
2843
Sandboxer
by: Sandboxer | last post by:
I want to be able to program Access to provide for me, by individual day, what my contract obligations are to my customers. Will Access recognize all the individual days in between a date range (simply a "from" date and a "to" date)? Additionally, I need to delivery a specific quantity of product when the customer's inventory is within about...
1
8249
by: vineetbindal | last post by:
Hi All, I am new to silverlight and i am following a tutorial. In order to connect to database in silverlight application i need to add a referance to a service. which gives me an error that the contract is not found. I had installed the iis after .net framework 3.5 but i have followed and done all the instructions given at microsoft's...
0
7504
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7435
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
0
7694
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. ...
0
7947
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that...
0
6026
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then...
0
5080
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert...
0
3491
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in...
1
1046
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
747
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.