Archives for : March2014

Abstract_factory.svg

Abstract Factory

If you follow good design principles and good dev practices and utilize the APIs and SDKS Microsoft has made available within the .NET Framework, you don’t need to know anything about patterns.

There, I said it. You don’t need to know anything about patterns.

Now why would I think this? Well, a few years ago I interviewed for company here in Tucson and had one of the best interviews I’ve ever had. But there was some confusion and the position was in Mountain View, and I’m in Tucson. And I had to decline the position. I was incredibly disappointed. The company was great, but most of all the people I had met were great. It was the best of all worlds.

So a couple of months later another position opened up at the same company with another development group. I applied and was asked to come in for an interview. Unlike my previous interview, which was just an hour or so and had felt more like a bunch of engineers hanging out shooting the breeze, this interview as far more formal.

In the first of the five interviews for the day, all three of the software engineers has sheets of papers with questions. After introducing themselves they started on the questions read right from the sheet of paper. Eventually they got to a question on patterns which was pretty simple, “What do you know about design patterns?” I said, “Well, I know about them cursorily as I read about them in a few of the blogs I follow, and I know that are established solutions to common problems, but I can’t think of any patterns off the top of my head.” The person that asked the question looked flummoxed. I’d like to think it was because up until this question I had been answering pretty well.

But I couldn’t answer a question on patterns.

Template_Method_pattern_in_LePUS3

The second and third interviews proceeded in the same manner. They would show up, ask me questions randomly off of the sheets of paper, things seemed to go pretty well until they got to the patterns question. They all asked the patterns question, I always answered the same way and I always got the same flummoxed look. I finished up the last two interviews, with the department head and hiring manager and went home. I was contacted a couple weeks later saying they weren’t interested in hiring me. The only thing that I could point to as a problem was the questions about patterns.

Fine, I need to know patterns. And so I get the ultimate design patterns book, “Design Patterns: Elements of Reusable Object-Oriented Software” by a group of software engineers know colloquially as the Gang of Four. In fact, in Uncle Bob’s papers on SOLID he quotes from the gang of four several times calling them the GoF. Wonderful! I’ve been a success software engineer up until now and knowing all about these patterns is going to make me even better. Right?

The design patterns in this book are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.
Design Patterns: Elements of Reusable Object-Oriented Software, Section 1.1, What is a design pattern?,
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides

Okay, so the first chapter is all about what a design pattern is, and how you would select the patterns and when to use them. There’s also a description on MVC in SmallTalk. I mean, I use MVVM regularly as a part of my every day development cycle in UI development, which is an extension of MVC, so I guess it’s kind of cool (for archival purposes?). There is also a brief description of a each of the design patterns in this first chapter.

And there is where it hits me.  You don’t need to know design patterns.  Chances are if you don’t know anything about them and you’re developing in .NET you are already using design patterns and don’t even know it.

So let’s see what the hell I’m talking about:

Creational:

Structural:

Behavioral:

Well, I’ll be a monkey’s uncle, the majority of patterns presented by the gang of four I already use in some form. Additionally, both composite and facade patterns, as well as the state pattern, I’ve used as solutions when architecting applications without understanding them to be design patterns, as such. They simply seemed like the obvious answer.

So I’ve decided after reading all about design patterns I don’t need to know about them. Read them yourself. Buy Design Patterns: Elements of Reusable Object-Oriented Software and you’ll see you don’t need to know them. Read the links above and once you understand them you’ll see you too don’t need to know them.

I mean, come on. If I was a horrible software engineer I wouldn’t have accomplished what I have and wouldn’t hold the position I do now. Knowing design patterns won’t make a bad software engineer good. So conversely, not knowing the patterns won’t make a good software engineer bad, right? Didn’t Uncle Bob say something like this?

Following the rules on the paint can won’t teach you how to paint.

This is an important point. Principles will not turn a bad programmer into a good programmer. Principles have to be applied with judgement. If they are applied by rote it is just as bad as if they are not applied at all.

Having said that, if you want to paint well, I suggest you learn the rules on the paint can. You may not agree with them all. You may not always apply the ones you do agree with. But you’d better know them. Knowledge of the principles and patterns gives you the justification to decide when and where to apply them. If you don’t know them, your decisions are much more arbitrary.

Getting a SOLID start, Robert C. Martin

So I guess Uncle Bob didn’t quite say what I said. He said you should know the principles and patterns. They’re another tool in your toolbox. So why would I think I don’t need to know anything about patterns? How have I gotten to where I am without knowing patterns?

It’s Andy’s fault

I blame Andy. See, I worked under Andy at my last company. Not in .NET but in Java. Andy grokked patterns. He could instantly recognize a scenario and apply the right pattern. It was fluid. Code flowed together. Problems solved. With Andy it was about the application of theory. He was one of the most prolific software engineers I’ve ever known. But here is what will really blow your mind, it was quality, clean, well-designed code. He was a truly incredible engineer that I learned an incredible amount from.

500px-FactoryMethod.svg

Factory Method

I learned design patterns but without understanding them as design patterns. Under Andy I learned that given problem X do solution Y but understand there will be constraint Z. I learned this by working in Andy’s code, extending it, fixing bugs (yes, even the best occasionally have bugs), being a dwarf standing on the shoulders of a giant. And then writing my own code under Andy’s review, having him point out problems and solutions.  Okay, so maybe it’s not really his fault.

I took what I had learned under Andy and starting applying it to doing development in .NET, recognizing parts of the framework that fit to a given pattern and applying it as the correct solution. When I use events in WinForms dev do I need to know that it is the Observer pattern? When I use a DataContractSerializer to write out files for serialized objects do I need to know that this is the Memento pattern? When I use a command in MVVM do I need to know it’s part of the Command pattern?

But ultimately this is the flaw to my argument.

  • It presupposes that we all work in .NET environment.
  • That we’re all developers who work in such a limited scope.
  • That we don’t need to think outside of the tray of ideas Microsoft has provided.
  • That Microsoft has provided for all problems.

Microsoft has applied design patterns to arrive at solutions that try and make our jobs easier but it doesn’t solve all my problems. As a software engineer I should be expected to be able to apply those ideas to any language and environment. Limiting myself to just the .NET environment does a disservice to myself. I am not merely a code monkey. I do more than just write code. I engineer systems. To do so I must understand design principles and patterns and I must be able to apply design principles and patterns in such a manner that makes the use and extension of that code easy to work with and maintain and that strives for a minimum of deficits.

So to bring this full circle, I had a bad interview, boo hoo. In the first interview it was more about how I would solve different scenarios and I gave solutions that I would later understand to be patterns. In the second interview it was a bad format with bad questions. I was blessed with a mentor who guided my early career in such a manner that I was taught sound design principles and patterns. Unfortunately we are not all blessed. And that is why you need to know design patterns.

Good design principles and patterns aren’t nearly as rampant as one would hope.

Start being part of the solution, understand, use and apply design principles and patterns.  Make your life easier.  Which by extension makes my life easier.

Thanks,
Brian

Image credits:
Abstract Factory
Template Method
Factory Method

Expanding on breakpoints

Originally I had a few posts planned to go over all the functionality of breakpoints. I’m surprised by how many of my fellow developers don’t understand the full range of abilities that breakpoints in Visual Studio provide. We all use breakpoints and most use conditional breakpoints but few seem to know about all the capabilities. I say, “originally I had a few points planned”, because while researching material for the post I ran across a series from Microsoft that goes over all the functionality of breakpoints. I don’t think I can expand further on what Microsoft has already written.

So without further ado, I would recommend you read,

To me I think the two must underutilized abilities of breakpoints are Function Breakpoints and Tracepoints.

Function breakpoints is really just putting a whole bunch of breakpoints across all of your code. For instance, referring you back to my series on the TPL and MVVM, I ended up with a bunch of samples all of which override “Run”. Say I wanted to step through all of the run methods to make sure everything is running as I expect? Well,

  1. just select the “New” dropdown in the breakpoint window
  2. select “Break at Function”
  3. enter “Run” as the function
  4. click “OK”
  5. click “All”
  6. click “OK”

and now breakpoints have been created on all the functions that matched that name. Of course a checkbox list is presented so I could individually select points in code rather than select all.

But say I was working on a bunch of UI stuff where there was an expected sequence of events to happen? And say that within that sequence, if I were to put in a regular breakpoint it would throw off the sequence so it no longer worked? Like if I was looking for or comparing mouse points or if the mouse was over another control, or had been moused out of a control? That is the perfect time for Tracepoints. Tracepoints output values to the debug window but optionally won’t actually stop (thus a tracepoint rather then a breakpoint). It’s kind of like putting in “Debug.WriteLine” but a whole lot cleaner, and it won’t accidentally get checked in.

Got any tips on breakpoints not mentioned in the posts above? Let me know.

Thanks,
Brian

SOLID – The Summary

Here is the list of all the posts in my series on SOLID.

SOLID – Getting Started and Source Material
SOLID – (SRP) The Single Responsibility Principle
SOLID – (SRP) Ignoring the rules on the paint can
SOLID – (OCP) The Open-Closed Principle
SOLID – (LSP) The Liskov Substitution Principle
SOLID – (ISP) The Interface Segregation Principle
SOLID – (DIP) The Dependency Inversion Principle

Going through this series has been fun. By far and way my favorite principle has to be the LSP. I think it’s because not only does it require an understanding of good OOD but also requires that you understand how your chosen language can affect different aspects of that OOD. As always I would encourage you to read the source material from Uncle Bob as there are some aspects he goes further in detail.

On a related side-note, given how derivative these posts were I did seek and get approval from Uncle Bob for this series where he said, “I am flattered. Please feel free to derive from my work so long as you provide appropriate citings.”

I originally had a new series on patterns scheduled but I think I’m going to make an executive decision and move those aside for a couple of posts on break-points.

Thanks,
Brian

SOLID – (DIP) The Dependency Inversion Principle

Previous post in this series:
SOLID – (ISP) The Interface Segregation Principle

A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS

B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.

The Dependency Inversion Principle, Robert C. Martin

The DIP is part and parcel with the OCP and LSP. Let’s start with a simple bit of source code based on Uncle Bob’s work.

Example 1: Copy Program (based on Figure 1 and Listing 2 of source material)

public class ReadKeyboard
{
	public string Read()
	{
		return Console.ReadLine();
	}
}

public enum OutputDevice { Printer, File }

public class CopyToDevice
{
	public void Copy(OutputDevice Device)
	{
		ReadKeyboard rk = new ReadKeyboard();
		while (true)
		{
			string result = rk.Read();
			if (result == string.Empty)
				break;
			if (Device == OutputDevice.File)
				WriteToFile(result);
			else if (Device == OutputDevice.Printer)
				WriteToPrinter(result);
		}
	}

	private void WriteToPrinter(string result)
	{
		/* throw new NotImplementedException(); */
	}

	private void WriteToFile(string result)
	{
		/* throw new NotImplementedException(); */
	}
}

It should be obvious now that this violates the OCP. Our CopyToDevice is dependent on ReadKeyboard and only gets input from the keyboard. Not only that but anytime we want to support a new output device we have to change CopyToDevice to support it. If our high level module, CopyToDevice, were to utilize abstractions rather then being dependent on the low level modules (i.e. ReadKeyboard, OutputDevice) then we wouldn’t have to worry about violating the OCP. Of course to do this the low level modules would have to implement abstractions that we could use in the high level, which therefore proves point A in the definition of the DIP.

Below is a solution that does not violate the OCP and adheres to the DIP.

Example 2: The OO Copy Program (based on Figure 2 and Listing 3 of source material)

public abstract class Reader
{
	public abstract string Read();
}

public abstract class Writer
{
	public abstract bool Write(string Value);
}

public class KeyboardReader : Reader
{
	public override string Read()
	{
		return Console.ReadLine();
	}
}

public class CopyToDevice
{
	public void Copy(Reader Reader, Writer Writer)
	{
		while (true)
		{
			string result = Reader.Read();
			if (result == string.Empty)
				break;
			Writer.Write(result);
		}
	}
}

I’ve left the higher level Writer as an exercise to the reader. CopyToDevice can work without be dependent on any specific writer or reader and won’t have to be modified anytime new devices are added. As Uncle Bob would say, “There will be no interdependencies to make the program fragile or rigid. And Copy() itself can be used in many different detailed contexts. It is mobile.”

Here is another sample relating to abstracting away the details so that your code doesn’t get locked into rigidity.

Example 3: Naive Button/Lamp Model (based on Figure 5 and Listing 5 of source material)

public class Lamp
{
	public void TurnOn()
	{
		ActivateElectricity();
	}

	public void TurnOff()
	{
		DeactivateElectricity();
	}

	public bool IsActivated { get; private set; }

	private void ActivateElectricity() { throw new NotImplementedException(); }
	private void DeactivateElectricity() { throw new NotImplementedException(); }
}

public class Button
{
	Lamp lamp;
	public Button(Lamp Lamp)
	{
		this.lamp = Lamp;
	}

	public void ChangeDetected()
	{
		if (lamp.IsActivated)
			lamp.TurnOff();
		else
			lamp.TurnOn();
	}
}

What’s interesting to me is that the above isn’t necessarily wrong or bad. Again this gets down to ignoring the rules on the paint can. If you have a very small project with a very small scope and per the requirements you never need to do anything other then what is above then there is nothing wrong with it. But in most cases that won’t happen. The problems here are obvious, we need to abstract away a lot of the details.

Example 4: Naive Button/Lamp Model (based very loosely on Figure 6 and Listing 6 of source material)

public abstract class ButtonClient
{
	public abstract void TurnOn();
	public abstract void TurnOff();
	public abstract void Toggle();
}

public abstract class Button
{
	public ButtonClient ButtonClient { get; private set; }
	public Button(ButtonClient ButtonClient)
	{
		this.ButtonClient = ButtonClient;
	}

	public abstract bool GetState();

	public virtual void ChangeDetected()
	{
		if (GetState())
			ButtonClient.TurnOff();
		else
			ButtonClient.TurnOn();
	}
}

public class Lamp : ButtonClient
{
	public override void TurnOn() { throw new NotImplementedException(); }

	public override void TurnOff() { throw new NotImplementedException(); }

	public override void Toggle()
	{
		if (IsActivated)
			TurnOff();
		else
			TurnOn();
	}

	public bool IsActivated { get; private set; }
}

public class ToggleButton : Button
{
	bool isDown;
	public ToggleButton(ButtonClient ButtonClient)
		: base(ButtonClient)
	{
		this.isDown = false;
	}

	public override bool GetState()
	{
		return isDown;
	}

	public override void ChangeDetected()
	{
		isDown = !isDown;
		this.ButtonClient.Toggle();
	}
}

Here we can see pretty clearly part B of the DIP that the details of the implementations are dependent on the abstractions.

To me, however, this last example is completely broken. It works under the assumption that a Button has a Lamp. And this doesn’t make any sense. Quite clearly a lamp has a button. I think this would be done cleaner with events (the observer pattern). This isn’t just because this is how it is actually done in the UI but because, again, the “has a” definition of objects.

Or maybe I’m over-analyzing this example. There is no doubt in my mind that this last example does a great job in demonstrating part B so maybe I just need to take it at that.

Again, higher level modules should not depend on abstractions and abstractions should not depend on details.

That’s it for the DIP and each of the individual principles. As I like to do, I’ll do a summary post next week with links to each of the posts in this series.

Thanks,
Brian

SOLID – (ISP) The Interface Segregation Principle

Previous post in this series:
SOLID – (LSP) The Liskov Substitution Principle

CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE.

When clients are forced to depend upon interfaces that they don’t use, then those clients are subject to changes to those interfaces. This results in an inadvertent coupling between all the clients. Said another way, when a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then the client will be affected by the changes that those clients force upon the class. We would like to avoid such couplings where possible, and so we want to separate the interfaces where possible.

The Interface Segregation Principle, Robert C. Martin

Unlike the other posts in this series I’m not going to include any code. It’s a theory only post. Uncle Bob’s material on the ISP, while good at explaining the ISP, has what I consider pretty poor code examples. In his other papers I understand how the examples of bad code could come about. For some reason I just find it hard to wrap my head around the bad code he demonstrates for the ISP. In his explanation of the bad code he even goes on to say, “Although this solution is common, it is not without problems.” But his example is so bad, how could it be common in OOD? Read his paper and tell me if you don’t agree with me. Regardless, know that even if the example is poor, the reasoning and purpose of the ISP is solid. (See what I did there? 🙂

The first thing to note on Uncle Bob’s quote above is that by interfaces Uncle Bob doesn’t mean only “interface” as in the C# keyword whereby we define a contract that the class will adhere to but also any base or abstract classes we extend from. Naturally than, if there is some base class we extend from that extends from another class (ad nauseam) that extends from some abstract class we are getting a lot of functionality but also a lot of overhead with each class. That is not to say that this is invalid but at each extension you have to understand that you are adding to the class and may be violating the SRP, the OCP and the LSP.

What the ISP is trying to correct is the injection of so much functionality into a single class that you end up with impacting the internal code causing the class to have to implement changes that override the original purpose of the class. So now, you may not have only violated the SRP but you risk violating the LSP.

This principle deals with the disadvantages of “fat” interfaces. Classes that have “fat” interfaces are classes whose interfaces are not cohesive. In other words, the interfaces of the class can be broken up into groups of member functions. Each group serves a different set of clients. Thus some clients use one group of member functions and other clients use the other groups.

The ISP acknowledges that there are objects that require non-cohesive interfaces; however it suggest that clients should not know about them as a single class. Instead, clients should know about abstract base classes that have cohesive interfaces. Some languages refer to these abstract base classes as “interfaces”, “protocols” or “signatures”.

The Interface Segregation Principle, Robert C. Martin

What we’re doing is trying to prevent so much downward pressure upon the purpose of a class from the clients that use it we end up making the class worthless because to make any changes to it would completely break any clients using it. Here’s the thing about SOLID, its purpose is to make your job easier. That may not be obvious. There is a lot of concepts here built from Uncle Bob’s experience (concepts that have proven useful in my own experience). What this means is that by extension, if you adhere to these concepts, than if I’m ever on a project your on, my job will be easier. All this is about trying to make our jobs easier.

So how do we prevent downward pressure? Well, the first way is the “Adapter” pattern (aka a wrapper). There is a good example in C# of the adapter pattern on wiki. The purpose there is essentially to modify a class in such a way that we as a client can use it without impacting any other clients that use it. Understand that this is huge. We get any specialization that we need out of the class without having to worry about the LSP and the OCP.

The second way is to simply judiciously apply interfaces. Where we need them, if it makes sense, extend from a class and implement the interface. There is nothing wrong with this. There are perfectly valid instances where we need separate interfaces.

Finally, Uncle Bob recommends the above, but with multiple inheritance, an option not available to us in C#.

If you’re dying for some sort of source code (though I would recommend anyways) read Uncle Bob’s source material this post is based on.

SOLID – (LSP) The Liskov Substitution Principle

Previous post in this series:
SOLID – (OCP) The Open-Closed Principle

FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.

The importance of this principle becomes obvious when you consider the consequences of violating it. If there is a function which does not conform to the LSP, then that function uses a pointer or reference to a base class, but must know about all the derivatives of that base class. Such a function violates the Open-Closed principle because it must be modified whenever a new derivative of the base class is created.

The Liskov Substitution Principle, Robert C. Martin

To me a part of the LSP is about understanding the subtleties that result of a language trying to handle OOP. The LSP seems obvious, code should be able to use a base class without any need of knowing about any derived classes. This post will cover a subtle and specific violation of the LSP that happens in C++, C# and other OOP languages. As with other code in this series it derives from Uncle Bob’s source material, porting his C++ to C#.

To get started, let’s begin with the idea of a “Is-A” relationship. In OOD generally we define parent/child relationships with a “Is-A”. For instance, a car is a vehicle. This means that vehicle would be the root or parent object and car would be a child. Uncle Bob’s source material (as noted in the citation above) uses “a square is a rectangle” relationship to show violations of the LSP.

Example 1: Square and Rectangle, a More Subtle Violation (based on source material)

public class Rectangle
{
	public double Width { get; set; }
	public double Height { get; set; }
}

public class Square : Rectangle
{
	public new double Width
	{
		get { return base.Width; }
		set
		{
			base.Width = value;
			base.Height = value;
		}
	}

	public new double Height
	{
		get { return base.Height; }
		set
		{
			base.Width = value;
			base.Height = value;
		}
	}
}

The code above is pretty straight forward. A square is a rectangle, right? Therefore a square should inherit from a rectangle. But a square is a special kind of rectangle. Whereas a rectangle can have any length to it’s width or height, a square must have the same width and height. To ensure that, when setting the height and/or width the square class will make sure that everything is even. But since Width and Height aren’t virtual, we have to use the “new” keyword on the Width/Height in square. Let’s throw together some quick code to test our squares.

Example 1a: Testing squares and rectangles.

public class TestRectangles
{
	public void Test()
	{
		Square s = new Square();
		s.Width = 1; //Height and Width set to 1
		s.Height = 2; //Height and Width set to 2

		ExampleF(s);
	}

	public void ExampleF(Rectangle Rectangle)
	{
		Rectangle.Width = 32;
	}
}

In the beginning of the Test method everything goes as expected. In the ExampleF method is where things fall apart. Setting the width in the ExampleF, even when a square is passed in, will only set the width. In Rectangle, because Width/Height is not marked as virtual, they cannot be inherited. To make sure you understand the consequences of overriding a value that is not marked as virtual, the C# compiler will issue a warning and tell you to use the “new” keyword so you are aware of what you are doing. So extending the base class without having Height/Width as virtual violates the LSP since a square is not really a substitution for a rectangle. The TestRectangles class would have to know that a square is a square to ensure that Width/Height remain the same.

Fortunately this is an easy fix, let’s identify Width/Height as virtual so we can substitute a square anytime we would use a rectangle.

Example 2: Square and Rectangle, The Fix (based on source material)

 public class Rectangle
{
	public virtual double Width { get; set; }
	public virtual double Height { get; set; }
}

public class Square : Rectangle
{
	public override double Width
	{
		get { return base.Width; }
		set
		{
			base.Width = value;
			base.Height = value;
		}
	}

	public override double Height
	{
		get { return base.Height; }
		set
		{
			base.Width = value;
			base.Height = value;
		}
	}
}

There is nothing above that any of us hasn’t run into. We know that it’s really easy to violate the LSP when we “new” properties, so we fix the issue by marking Width/Height as virtual and truly override the properties. But, does this really make sense? Consider if I was a new software engineer and I didn’t really understand the subtleties of the LSP. Let’s add a method to our test class that anyone of us might write.

Example 2a: Testing squares and rectangles

public class TestRectangles
{
	public void Test()
	{
		Square s = new Square();
		s.Width = 1; //Height and Width set to 1
		s.Height = 2; //Height and Width set to 2

		ExampleF(s);
		ExampleG(new Square());
	}

	public void ExampleF(Rectangle Rectangle)
	{
		Rectangle.Width = 32; //Height and Width set to 32
	}

	public void ExampleG(Rectangle Rectangle)
	{
		Rectangle.Width = 5;
		Rectangle.Height = 4;

		Debug.Assert(Rectangle.Width * Rectangle.Height == 20);
	}
}

Here we have added “ExampleG”. The developer of this method made an assumption that I think any of us would. We should be able to set the width and height of a rectangle and get the area expected. But in this case the Assert will throw. So we’re back to a violation of the LSP where an operation that works on a Rectangle will not work on a Square.

Validity is not Intrinsic

This leads us to a very important conclusion. A model, viewed in isolation, can not be meaningfully validated. The validity of a model can only be expressed in terms of its clients. For example, when we examined the final version of the Square and Rectangle class in isolation, we found that they were self consistent and valid. Yet when we looked at them from the viewpoint of a programmer who made reasonable assumptions about the base class, the model broke down.

Thus, when considering whether a particular design is appropriate or not, one must not simply view the solution in isolation. One must view it in terms of the reasonable assumptions that will be made by the users of that design.

The Liskov Substitution Principle, Robert C. Martin

So what is the solution? Uncle Bob discusses that the issue here is that when considering the “Is-A” relationship you need to consider the behavior of the objects. If we’re going to treat child classes as the parent classes we need to understand the behavior of the objects. In the case of our Square, it does not behave like a Rectangle. By the literal definition a square is a rectangle. But for all practicality that doesn’t work.

Now, stepping away from the source material, we can force things to work by utilizing immutability, making it obvious to any users exactly how the object operates.

Example 3: Squares and Rectangles

public class Rectangle
{
	public double Width { get; private set; }
	public double Height { get; private set; }

	public Rectangle(double Width, double Height)
	{
		this.Width = Width;
		this.Height = Height;
	}
}

public class Square : Rectangle
{
	public Square(double Side)
		: base(Side, Side) {}
}

The above code works in that it allows us to treat a Square as a Rectangle. The LSP is held up. The problem here is that we’ve done it by removing any behavior. Remember that the problem with the original code was that a square behaved differently then a rectangle. So why not do this? Well, I have to fall back to the quote from above. Working in isolation were we rigidly apply the rules of OOD can actually lead to cases where we violate them. Through your own understanding and experience of the rules of OOD as well as those around you, you have to know when to ignore the rules on the paint can. In this instance there may very well be a reason to implement the code as it is. I can also come up with with several cases where this rigidity can cause future problems.

Uncle Bob’s source material discusses utilizing “Design By Contract” where we can define pre- and post- conditions that would have fallen apart with our samples above. It also goes into a a real example from Uncle Bob about where he had a problem with the LSP and actually ended up with a rather hackey solution that violates the LSP. As with all these posts covering the SOLID principles, I would encourage you to read the source material to get a bigger-picture understanding of the SOLID principles.

Thanks,
Brian