• No results found

Leanpub.more.Coding.in.Delphi.2015

N/A
N/A
Protected

Academic year: 2021

Share "Leanpub.more.Coding.in.Delphi.2015"

Copied!
231
0
0

Loading.... (view fulltext now)

Full text

(1)
(2)

Nick Hodges

This book is for sale athttp://leanpub.com/morecodingindelphi This version was published on 2015-12-06

ISBN 978-1-941266-06-9

This is aLeanpubbook. Leanpub empowers authors and publishers with the Lean Publishing process.Lean Publishingis the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.

(3)

Please help Nick Hodges by spreading the word about this book onTwitter! The suggested hashtag for this book is#morecodingindelphi.

Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter:

(4)

Foreword . . . . i

Preface . . . . ii

Acknowledgments . . . . iv

Frameworks Used in this Book . . . . v

Spring for Delphi Framework . . . v

DuckDuckDelphi . . . v

Six Thoughts Before We Start . . . . 1

Thoughts on Encapsulation . . . 1

Thoughts on Coupling . . . 5

Thoughts on Cohesion . . . 6

Thoughts on Command Query Principle . . . 7

Thoughts on Postel’s Law . . . 9

Thoughts on Composition over Inheritance . . . 10

Conclusion . . . 13

Writing SOLID Code . . . . 15

Introduction . . . 15

What SOLID Is . . . 15

Single Responsibility Principle . . . 16

Open/Closed Principle . . . 18

Liskov’s Substitution Principle . . . 20

Interface Segregation Principle . . . 23

Dependency Inversion Principle . . . 26

Conclusion . . . 30

Patterns . . . . 31

Factory Pattern . . . . 32

Introduction . . . 32

An Informal Look at Factories . . . 32

A More Formal Look at Factories . . . 42

Factory Method . . . 42

(5)

Conclusion . . . 47

Observer Pattern . . . . 48

Introduction . . . 48

Baseball Data . . . 48

Generic Observer with Spring4D . . . 54

Conclusion . . . 57

The Adapter Pattern . . . . 58

Introduction . . . 58

A Simple Example . . . 59

A More Practical Example . . . 62

Conclusion . . . 65

Decorator Pattern . . . . 66

Decorator and Interfaces . . . 70

Summary . . . 75

Command Pattern . . . . 77

Introduction . . . 77

A Simple Example: An Auto Key Fob . . . 77

Undoing Commands . . . 82

A Simple Queue of Commands . . . 86

Summary . . . 89 Operator Overloading . . . . 91 An Example:TFraction . . . 92 Assignments . . . 96 Implicitvs.Explicit . . . 97 Using TFraction . . . 98 Conclusion . . . 99

Multi-Threading and Parallelism . . . 101

Introduction . . . 101

About These Three Chapters . . . 101

What is a Thread? . . . 101

Think Differently . . . 102

Why Should I Care About Multi-threaded and Parallel Programming? . . . 103

Multi-threading withTThread . . . 106

Introduction . . . 106

Descending from TThread . . . 106

Thread Termination . . . 111

Handling Exceptions in Threads . . . 114

TThread Class Methods . . . 116

Synchronization . . . 118

(6)

The Perils of Multi-threading . . . 127

Conclusion . . . 128

Parallel Programming . . . 129

Parallel Programming Library . . . 129

ParallelForLoops . . . 146

Introduction . . . 146 Conclusion . . . 159 Aspect-Oriented Programming . . . 160 Introduction . . . 160 An Illustration . . . 161 The Basics: . . . 162

A More Useful Example . . . 168

AOP in a Single Place . . . 171

AOP via Attributes . . . 173

Conclusion . . . 174

Component Writing . . . 175

Introduction . . . 175

TSmiley – A Brief History . . . 175

Start from Scratch . . . 177

Conclusion . . . 196

Appendix A: Resources . . . 197

Source Control . . . 197

Patterns . . . 198

Appendix B: Duck Typing . . . 199

Introduction . . . 199

What is Going on Here? . . . 199

Duck Typing with DSharp . . . 200

Duck Typing with DuckDuckDelphi . . . 203

Duck Typing in the VCL . . . 207

Why Use Duck Typing? . . . 208

Problems with Duck Typing . . . 208

Conclusion . . . 209

Appendix C: Stuff Nick Does When Coding . . . 210

Formatting Stuff . . . 210

Spacing . . . 210

Coding Stuff . . . 214

Conclusion . . . 217

Appendix D: Sources Used in Writing This Book . . . 218

(7)

Web Links . . . 218 Videos . . . 219

(8)

I first met Nick Hodges at a meeting of the Naval Postgraduate School’s PC Computer Club the early 1990’s. I drove down to the meeting from Scotts Valley to present Borland’s developer tools (which included Turbo Pascal for Windows, Borland Pascal, Turbo C++ and Borland C++). In those early days we had the Turbo Vision and OWL class libraries for writing DOS and Windows applications. By the middle of the 1990’s the VCL component architecture (Properties, Methods, Events) was added to move GUI programming to the next level for Pascal and C++ developers. Even with these extensive class and component libraries, developers still needed to focus a majority of their time on their code, the real business logic of an application.

I have had the privilege of working closely with Nick Hodges since those early days when he was a member of our developer community and also as a fellow employee. Nick has always had a passion for programming, developer tools and the community of developers who love what we can do with the tools and code. Nick has also shown the development world new tips, tricks and techniques at our live conferences, online events, technical articles, blog posts and videos.

In his first book, “Coding in Delphi”, Nick shared even more of his coding experiences to the world showing developers the power and new innovations in the Object Pascal programming language including: interfaces, generics, attributes and anonymous methods. Beyond new language syntax, Nick went on to showcase best practices for exception handing, using the extended runtime type information system and exception handling. To finish the book, Nick documented his years of experience modernizing legacy applications while keeping them stable using dependency injection and unit testing.

In this new book, “More Coding in Delphi”, Nick brings us all to an even higher level of programming expertise covering patterns, operator overloading, parallel programming, multi-threading, aspect oriented programming and component development.

It is my honor to call Nick Hodges my friend. More importantly, I am an avid Nick Hodges student of programming. Put together, these two books provide the largest amount of deep Delphi coding expertise covering the most important programming topics.

I know you will enjoy reading and learning about “More Coding in Delphi”. David Intersimone “David I”

Vice President of Developer Relations and Chief Evangelist Embarcadero Technologies

August 1, 2015

(9)

Well, here we are again.This book is to follow my first book, Coding in Delphi. I was humbled and pleased by the reception it received, and so I decided to give book writing another shot.

The first book didn’t have many screen shots of forms and such. The focus was on the code. Ultimately, that’s what we developers produce, right? I thought it would be a good idea to write another book of the same ilk because, well, the first one worked out really well and because there was a lot more to write about. However, this one will have a few screen shots in it, as there are some coding principles – parallel programming comes to mind – where a simple console application won’t quite illustrate things well. So this book will have some VCL applications in the demo code.

I’ve actually broken down and included an entire chapter on VCL component writing. My friend, Bob Dawson, suggested that it has been quite a while since anyone wrote about components, and I decided he was right. So we’ll get a chapter covering the latest techniques on building the immortal TSmiley.

But the focus will remain, as always, on the code.

One of the reasons that I’ve written these books is that they force me to learn new things. I’ll be honest: at the start of the first book, I didn’t always know enough to write a complete chapter on the topic at hand (cough, parallel programming, cough, cough). But I used writing about the topic as an opportunity to learn all I could and share it with you. The same is true of this book – I’ve learned a lot in writing it. This seems to work well, as we both end up smarter. I love learning, and I assume you do, too, because you bought this book. They say that you never really know a topic until you have to teach it, and these books are my way of teaching to learn, in addition to giving back to the Delphi community that has given me so much.

Another aspect to this book is that it uses a number of open source frameworks to teach the principles and techniques within. One of the objections that I hear from developers about using some of the tools and frameworks included in these books is that they add “bloat” and make binaries bigger. While I might not agree with the characterization that comes with the term “bloat,” I have no choice but to concede the point about binary size. However, I’m going to press forward with the assumption that binary size isn’t an issue. You can’t make an omelet without breaking a few eggs, and in this case, the broken eggs are an expansion of generated code. For instance, if you want to use Duck Typing in your application, you’ll need to enable RTTI, and that is going to increase the size of your binary. But without that increase, you don’t get Duck Typing. It’s a price that must be paid for cleaner, more powerful code.

The same thing could be said for performance. Much of what is discussed here won’t make for the most blazingly fast code ever written, but blazingly fast code isn’t always the goal. If speed is your deal, you’ve likely come to the wrong place. My friend, Bob Dawson, refers to the battle between two types of developers: those that want speed and performance and would have Delphi be more like C++, and those that want Delphi to be more like C#, with more powerful language features and libraries as part of the system. I fall in the second camp, and this book, as did my previous book, will reflect that. Speed is nice, but clear, powerful code is what I’m interested in.

Some things to note:

(10)

• I’m going to assume in this book that you have read my first book, Coding in Delphi. If you haven’t done so, I very strongly recommend thatyou head over to the book’s website¹, buy a copy and read it first. There will be things I talk about here that assume you know the ideas and principles found in the first book. In addition, my demo code may make use of things like the theSpring4D²collections and other frameworks discussed in Coding in Delphi.

• All of the code and this entire book is written about the Windows compiler. We won’t be covering any of the newer features added to the mobile compilers in more recent versions of Delphi.

• This book was written with Delphi XE8 as the main development platform – most of the code will very likely work with earlier versions, but some of it won’t. Specifically, the Parallel library is found only in Delphi XE7 and above, so for that chapter you’ll have to have at least XE7.

There’s a further point that I want to emphasize that I don’t think I made clear enough in my previous book. The techniques that I describe here and in my previous book should generally be applied to green-field code. By that, I mean “code that you write from scratch.” I’m pleased that I’ve helped some people “see the light” on writing clean, decoupled code, but you can only take these principles so far.

For instance, I urge you to code against interfaces and not implementations. But I wouldn’t argue that you should wrap every component in an interface. I would argue that when you start writing your domain objects and business classes, you should write against interfaces, but for things like already existing code or components, you can use those as is. There’s no need to try to put your datamodules into the Spring4D Container. The bottom line – if it seems hard to do, then maybe you shouldn’t be doing it. Writing clean code should flow naturally, and if you find yourself fighting to do it, then perhaps you need to rethink your approach.

In any event, the goal here is as before: to help you write clean, uncoupled code with your concerns neatly separated and your code highly maintainable and testable. I hope that this book does that for you like the last one did.

¹http://www.codingindelphi.com

(11)

First of all, I’d like to acknowledge and thank my beautiful wife, Pamela, who put up with me sitting at Burger King getting jacked up on caffeine via iced tea and working on this book. She’s a marvelous woman, and I love her with all my heart. I’m grateful to her. I’d also like to thank my three kids who didn’t see their dad for all the time I was working on this book. I love you more than you can know until you have kids of your own. I’d like to thank all the people who bought my first book. Its success inspired me to keep writing and produce this book. I hope it is as well received as the first one was. Thank you.

I’d like to thank the fine people at the Burger King on North Charlotte Avenue in Pottstown, PA, for their kind service and patience with that strange guy who came in on Saturday and Sunday mornings and sat there at the computer station with his laptop and typed away. You guys were all great.

Many people – mostly members of the Google Group for Coding in Delphi – read drafts of this book and helped make it better. Some folks went above and beyond, and I’d like to make note of them here:

• Andris Klaipins • Asbjørn Heid • Andrea Raimondi • David Milllington • Stefan Glienke Some specific shout-outs:

Thanks very much to David Heffernan for his help in fleshing out theTFractionexample in the Operator

Overloading chapter. I’m grateful for his help.

RemyLebeau(the guy from TeamB, not the guy from the X-Men) helped me immensely in writing the chapter on the Parallel Programming Library. I’m grateful for his help, too.

Thank you to Danny Wind for letting me use his demo in theTParallel.Forchapter. I’m grateful.

The parallelism chapters were made much better by the careful and thorough reading of Andrea Raimondi and Asbjørn Heid. I’m grateful.

David Millington and Asbjørn Heid went over theTThread chapter with a fine tooth comb, improving it

immensely. I can’t say enough about how helpful they were. I’m grateful.

Jason Southwell and David Millington also made significant contributions to the Factory Pattern chapter. I’m grateful for their help.

I’m quite sure I’ve forgotten someone who helped me in some significant way. If that is the case, please accept my thanks and apologies for forgetting you. And yes, I’m grateful. :-)

I’d also like to thank Diane Moser for her awesome proof-reading skills. As always, any errors are totally mine.

(12)

This book is about writing code in Delphi. As a result, it uses a number of open source frameworks to facilitate the coding techniques that are discussed herein. I refer to them here at the beginning of the book to enable you to retrieve the code and get them set up before we begin.

All of the frameworks below are available via source control. I recommend that you pull or clone the source code in accordance with the instructions in the source repository. This way you can very easily keep up with the latest changes and fixes. If you have questions about how to setup and use Subversion, Git, or Mercurial, please see Appendix A.

Spring for Delphi Framework

The Delphi Spring Framework is a large collection of libraries, including data structure collections, a dependency injection container, interception, encryption, and other features. In this book we’ll be closely examining the interception library.

Homepage:http://www.spring4d.org/³

Source Location:https://bitbucket.org/sglienke/spring4d⁴ Repository Type: Git

License:The MIT License⁵

DuckDuckDelphi

DuckDuckDelphi is an open source framework that provides duck-typing to Delphi developers. Homepage:http://arcana.sivv.com/duckduckdelphi⁶

Source Location:https://code.google.com/p/duckduckdelphi/⁷ Repository Type: Mercurial

License:Apache License V2.0⁸

³http://www.spring4d.org/ ⁴https://bitbucket.org/sglienke/spring4d ⁵http://opensource.org/licenses/mit-license.php ⁶http://arcana.sivv.com/duckduckdelphi ⁷https://code.google.com/p/duckduckdelphi/ ⁸http://www.apache.org/licenses/LICENSE-2.0 v

(13)

As we begin the book, I want to give my thoughts on a few basic, only loosely related topics that I think are useful and of interest. Those topics are:

• Encapsulation • Coupling • Cohesion

• Command/Query Separation • Postel’s Law

• Composition Over Inheritance

Thoughts on Encapsulation

Introduction

I remember quite well when the “light bulb went on” when it came to object-oriented programming (OOP). It came while I was reading my favorite technical book of all time, Borland Pascal 7 Insider by Paul Cilwa. It was a terrific book, full of excellent writing and exceedingly useful code. It illustrated beautifully the use of objects, including encapsulation, inheritance, and the hardest notion, polymorphism. It was a thrill to finally understand what all the hype was about – and to realize the hype really was worth it.

When someone approaches OOP, I think the first and easiest concept to get is encapsulation. In its most basic form, encapsulation is “information hiding” – the concealment of the internal workings of a class. New developers seem to get that notion pretty quickly. They then move on to inheritance and polymorphism. Encapsulation looks pretty simple in Delphi:

type TPerson = class private FFirstName: string; FLastName: string; public

property FirstName: string read FFirstName write FFirstName;

property LastName: string read FLastName write FLastName;

end;

Easy, right? The internal data is private and protected from prying hands, while it is safely exposed as a property. Couldn’t be simpler.

(14)

Implementation Hiding

I think that encapsulation is given short shrift as the “easy” part of OOP. I think that it is more important and more nuanced than it is given credit for. Sure, it’s about information hiding. That’s the basic idea, naturally. But there’s more to it. I think that instead of thinking about encapsulation as “information hiding,” we should be thinking about it as “implementation hiding.”

Consider this class:

type

TCustomers = class private

FList: IList<TCustomer>

public

constructor Create; // will create the list...

property CustomerList: IList<TCustomer> read FList;

end;

Does this class really hide its information? Is it protected from prying eyes and over-zealous developers? No, it isn’t. Think about this code that could easily be written using this class:

MyCustomers.CustomerList.Clear;

Is that something that you want the consumer of the class to be able to do? Did you even stop to think that the way the class was designed would allow for such a thing? You may have hiddenFList from your consumers, but you haven’t hidden it enough; when you expose it as a property, you’ve exposed all of it. You haven’t hidden the implementation – you’ve actually exposed it.

True encapsulation would take things a step further. Consider the following code:

type

TCustomers = class private

FList: IList<TCustomer>;

function GetCustomerList: IEnumerable<TCustomer>;

public

constructor Create; // list will be created here

procedure AddCustomer(aCustomer: TCustomer);

property Customers: IEnumerable<TCustomer> read GetCustomerList;

end;

There are two important things to note here. First, the list is no longer exposed for change. Instead, it is exposed as an IEnumerable<TCustomer>, giving you full power over the observation of the list, but no

power to change the list. Second, there is a single method,AddCustomer, that allows the user of the class to

add customers. That’s it – they can’t do anything else to the list. They can’t delete or alter the items in the customer list in any way. They can only read the list via theIEnumerable<T>interface and add customers

(15)

This is true encapsulation – you’ve completely protected the list and given access to it in a safe way. You’ve limited the ability to change the list merely to adding new customers. Thus, you’ve protected the internal implementation while still providing the functionality you need. If you want to be able to delete customers, you can add a procedure that does that without losing any of that protection.

Sweet. A Balance

You need to balance the need for information hiding and implementation protection against the class’s usability. You could provide protection for everything by making all your members private, but then of course your class wouldn’t be able to do anything. You could make everything public, but then of course you would be neither protecting nor hiding anything. The trick is to find the right balance.

A good rule of thumb is to only make something public if it is needed for the use of the class and if it protects the internals of your class. Nothing should be made public that threatens the internal integrity of your class. The balance also comes in when you consider that the public portion of your class should make it obvious what your class does and can do, and what it shouldn’t do, as well. A class designer should be very conscious of what is being revealed and take great care to ensure that all internals are properly protected, but properly exposed when necessary.

Never Let a Bad State Happen

Another aspect of encapsulation that gets short shrift is the notion that a class should never allow itself to be put into a bad or non-functional state. Encapsulation dictates that you not only hide your internals, but you protect them from being placed in bad or impossible states.

The first thing this means is that no internal object instance should ever be exposed as nil. If you have an object as an internal member, you should ensure that it is created before it is needed, and that it remains created and valid throughout the time that it is exposed to the end user.

..

Note that this position allows for “lazy” initialization of objects, that is, the creation of objects on demand rather than on the initialization of the object. Your internal pointers can be nil, but that fact should never be exposed to the consumer of your object.

(16)

function GetListOfWidgets: TWidgetCollection; begin if Widgets.Count = 0 then begin Result := nil end else begin

Result := Widgets; // Of type TWidgetCollection

end;

end;

This function could return nil. Therefore, any code that consumesGetListOfWidgetshad to check fornil

every time. This can be tedious. Rather than returningnil, the code should instead return an empty list to

indicate that there are no widgets in the collection.

A class should never be allowed to present itself to the consumer asnil. Normally, this means you should

create your internal objects in the constructor of the containing class and never free them until the destructor is called. If you have some need to “reset” your class during its lifetime, then have a routine that does that explicitly. Always protect your internal classes so that a consumer can never see a bad state – especiallynil.

Dependency Injection

But what about the case of Dependency Injection? In Coding in Delphi, I strongly encouraged you to ask for your dependencies rather than create them. If a reference should ever benil, then you need to be very careful

about what you accept from external sources. Consider the following class interface:

type

TWidgetProcessor = class private

FWidgetVerifier: TWidgetVerifier;

public

constructor Create(aWidgetVerifier: TWidgetVerifier);

end;

What should the constructor look like? You may be tempted to make it look like the following:

constructor TWidgetProcessor.Create(aWidgetVerifier: TWidgetVerifier);

begin

inherited Create;

FWidgetVerifier := aWidgetVerifier;

end;

That looks great, right? But what happens if the user passes in anilreference? Well, you’ll end up with a nilreference as an internal and that’s not good. Instead, you need to protect your internals from ever being nil:

(17)

constructor TWidgetProcessor.Create(aWidgetVerifier: TWidgetVerifier);

begin

if aWidgetVerifier = nil then begin

raise ENilParameterException.Create('Cannot pass a nil parameter');

end;

inherited Create;

FWidgetVerifier := aWidgetVerifier;

end;

This is called the “Guard Pattern,” and the notion is pretty simple: you “guard” against bad data trying to infect your internals. You “Fail Fast” in ensuring – right at the point of entry – that your internals can never benil. The Spring for Delphi Framework has a class calledGuardin theSpring.pasunit that performs this

protection for you in one line of code. Here’s an example:

constructor TWidgetProcessor.Create(aWidgetVerifier: TWidgetVerifier);

begin

Guard.CheckNotNull(aWidgetVerifier, 'aWidgetVerifier');

inherited Create;

FWidgetVerifier := aWidgetVerifier;

end;

The Guard class has a number of class procedures that allow you to verify input in any number of ways, ranging from nil checking to making sure strings aren’t empty and that anyBooleancondition is eitherTrue

orFalse. Note that the check in the example is done even before the call toinherited. If the condition is

not properly met, the framework will raise anEArgumentException, using the second parameter as part of

the error message to tell you which parameter was not valid. The first parameter is obviously the item to be validated.

Whether you choose to use the Guard class or write your own Guard code, it is imperative for proper

encapsulation that you use a guard clause to protect your internal classes and other references from ever becoming nilor any other state where they are anything less than usable. Part of the contract you make

when you provide a class interface is that the class will always work, and if you allow your class to get itself into an unusable state, then you are breaking the contract you have with the consumer of your code. Thus, encapsulation becomes a touch more important than mere “information hiding,” eh?

Thoughts on Coupling

In Coding in Delphi I talked a lot about coupling, but I feel like I assumed that you knew what it meant. I’m guessing most of you do, but I thought I’d talk about it a bit here in more detail.

Coupling is the measure of how dependent your code modules are on each other. Strong coupling is bad and weak coupling is good. Strong coupling means that your modules cannot be easily separated from each other, due to the fact that the internals are mixed and intertwined. When your system is strongly coupled, it is said to be “spaghetti” code, as everything is all mixed up together like a bowl of spaghetti noodles.

Strong coupling often means that a change in one place can have unforeseen effects in unknown other places. It means that your code is harder to understand because complex, intertwined relationships are difficult to

(18)

understand. Strongly coupled code is difficult to reuse because it is difficult to extract it for use elsewhere due to of all the intertwined dependencies. One should strive to reduce coupling in one’s code to as much as possible.

Of course, your code can’t be completely decoupled. A collection of completely decoupled modules can’t do anything. They need to be coupled in a thin and light manner. I’ve said before that an interface is like a wisp of smoke – there is something there, but it is really hard to grab onto it. Code that is coupled via interfaces is coupled as thinly as it can possibly be coupled in Delphi.

A real world example of heavy coupling is the Space Shuttle. Now I’m no rocket scientist, but my guess is that the Space Shuttle is made up of hundreds of thousands – if not millions – of unique parts that fit together in one way and one way only. The parts are not generally reusable, and if you need to alter one part – especially if you have to alter the interface of a part – you have a problem because the other parts around it will probably need to be altered as well. This initial alteration could spread a long way throughout the craft and have far-reaching ramifications. The Space Shuttle, then, is a highly coupled system with high cohesion. What’s an example of loose coupling? How about a Space Shuttle built out of Legos. Sure, it won’t go anywhere, but it would be easy to change its design as Lego pieces are easily put together and taken apart to make whatever you want. A Lego Space Shuttle would have low coupling.

Most of what I wrote about in my previous book and much of what I’m writing about in this book will be about reducing the coupling of your code.

In summary, uncoupled code will be:

1. Easier to read because it will be simple and not complex.

2. Reusable because it will be connected only by very thin interfaces

3. Easy to change and maintain because changes to the code will be isolated.

Thoughts on Cohesion

Well designed code will have good cohesion, but I really didn’t mention this facet of developing clean code in my previous book. I thought it would be a good topic to cover here.

What is cohesion? Wikipedia defines it as the “degree to which the elements of a module belong together⁹” High cohesion is considered a good thing. That is, it is a good design to have your classes work well together to form a whole.

An example of good cohesion is an electric drill. It is made of a motor, a power supply, a casing, a trigger, a holder for drill bits, as well as the drill bits themselves. These separate components –- as different as they are individually –- all work together to create something useful.

The same is true for your code: if your classes can be easily combined together to create something useful, then that code is said to be cohesive. There’s one catch, though: as we saw in the previous section, you don’t want to achieve cohesion at the expense of coupling. A properly designed system has good cohesion and is loosely coupled.

(19)

Think about your drill. I bet a well designed drill can be easily taken apart using the proper screwdriver. The motor can be removed from the casing, the electrical system easily fixed or replaced, the drill bit holder removed and repaired, and then the entire drill put back together again. This happens because the drill is both loosely coupled together – each part is replaceable – and has high cohesion among its component parts. Your code should be the same way: as was said above, it should be loosely but properly coupled. The modules you build should be able to be composed together to form something useful, but in a way that makes the system’s decomposition relatively easy. If you realize that a piece of your system is useful for another project, you want to be able to easily reuse that module elsewhere without baggage from the current project. This is what good coupling is about: easily fitting together modules that were not originally intended to do so. As mentioned above, Legos show high cohesion. They can easily be pieced together to create almost anything you can imagine. Even Duplo blocks have an interface to regular Legos that makes them very cohesive and easy to combine. Legos can be taken apart very easily. They can be put together very easily. There is a reason why they are the one of the most popular toys on the planet. Legos exhibit low coupling and high cohesion – the main reason that they are so popular.

Your code should be like Lego blocks – easy to combine to create useful things.

Thoughts on Command Query Principle

In keeping with our previous discussion on encapsulation, let’s look at the notion of separating “commands” and “queries” by using the Command/Query Principle. The notion was first discussed by Bertrand Meyer in his bookObject Oriented Software Construction¹⁰. This means that the idea is not new. In its basic form, it means we should separate the things that read data from the system and the things that write data to the system.

The Command/Query Principle states that there should be a clear separation between the updating of information and status in your program and the way that you read information from it. Commands and queries should be separately declared (though you’ll find that commands can call queries but not vice-versa). It can be as complex as ensuring that reading and writing take place in completely separate classes, or as simple as ensuring that commands and queries are put in different methods of your classes.

Of course, the first question you’ll have is “What do you mean by ‘command’ and ‘query’?” Well, I’ll tell you. Queries

A query is an operation that returns a result without changing the state of the class or the application. In Object Pascal, this is typically a function. As a general rule, queries should not change the state of a class. They should be “idempotent,” meaning “denoting an element of a set that is unchanged in value when multiplied

or otherwise operated on by itself.” (I had to look that up, by the way….)

Queries should exhibit two main behaviors: they should return a single value and “asking a question should not change the answer.” They should be referentially transparent, which means that they should be perfectly replaceable with their literal result without changing the meaning of the system.

¹⁰http://www.amazon.com/gp/product/0136291554/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0136291554&linkCode= as2&tag=nickhodgeshomepa&linkId=IVMYKYZ22AWZJ2LQ

(20)

Your functions shouldn’t change the status of the system you are working on, whether that be a class, a framework, or an application. You should be able to run a query, i.e. a function, a hundred times in a row and get the same answer back each time when the input is the same. This makes calling a function at any time possible without repercussions, because it doesn’t change the status of the system.

Commands

A command is any operation that has an observable side effect. It is any code that changes something in your class or application. Typically, in Object Pascal a command will be a procedure – that is, code that takes actions without returning a value. Commands can call queries (but queries should never call commands, because commands change the status of the system).

Commands should not in general return values. Thus, the use of varparameters should be discouraged, if

not outright banned. Don’t Mix the Two

As a general rule, all your methods and routines should be easily identifiable as either a command or a query. Commands and queries should be separate entities in your code – with the exception that a command can call a query if need be. A query can’t call a command, because commands change the system, and that should really not be allowed.

The use of varor outparameters in a procedure will confuse this issue and thus should be discouraged.

Following the above rules, you should more easily be able to understand and modify your code.

The Command/Query Principle also encourages you not to violate what I consider to be a bedrock of sound development technique: don’t try to make one thing do two things. Here is some code that does exactly that:

procedure ProcessWidgets(aCollectionOfWidgets: TWidgetCollection; out aNumberOfProcessedWidgets: integer);

This method is a clear violation of CQP as it obviously is trying to be a command and alter the state of the system by processing widgets, but it also tries to be a query by returning, through anoutparameter, the

number of processed widgets.

Instead, the system should have a simple command to process widgets and a separate query to return the number of widgets that were processed. The procedure is trying to be two things at once, and all kinds of mischief comes from making one thing do two things.

For instance, instead, the code should probably look something like this:

procedure ProcessWidgets(aCollectionOfWidgets: TWidgetCollection);

function CurrentProcessedWidgetCount: integer;

In this way, you can process widgets all that you want, and any time you want, you can check the state of processed widgets. But you don’t do both in one statement, which would be a violation of the admonition to have one thing do one thing.

Following the CQP in the design of your code will help to ensure the proper separation of concerns, resulting in cleaner, easier to read and easier to maintain code.

(21)

Thoughts on Postel’s Law

Jon Postel wrote an early specification for the Transmission Control Protocol (TCP), one of the core protocols of the Internet protocol suite. You use it every day to surf the web, send emails, etc. One of the guiding principles that he used when writing it was this:

Be conservative in what you do, be liberal in what you accept from others.

This idea is also called the “Robustness Principle”, and is sometimes rephrased as “Be conservative in what

you send; be liberal in what you accept.” When applied to TCP, it means that the sender of data should be

strict in what is sent, ensuring that it is accurate and precise. It also means that the receiver of data should be forgiving and understanding of data to as large a degree as possible. If you send data, be as clear as possible in what you send. If you can accept sent data, then you should.

Any application programming interface (API) should follow this principle. The same principle should apply to your code. The public interface of your class should be viewed as an API. It should be conservative in what it sends and forgiving in what it receives. When calling another API, your code should send out correctly formatted data, following the rules laid down by the API. It should, however, be willing to receive incorrectly formatted input as long as that input can be understood.

You shouldn’t be so liberal in accepting input that you allow things like SQL injection and buffer overflow exploits. Take care to protect yourself while you are being forgiving in what you accept. For instance, if passed a string, you might be happy to accept strings with blank spaces on the beginning and end, and use the Trim function to clean things up for the sender. Consider the situation where the input is a name. Clearly, a name should not have spaces at the beginning or end. However, you should accept a string that has such spaces. Why would you reject it? You wouldn’t. Nor would you leave it in the form it was sent to you before accepting it.

Another example? Paths. Say you have a function that accepts a path as a parameter. Say,

procedure WriteFileToDisk(const aPath: string; const aFilename: string);

This procedure should be able to deal with a path that either has or does not have the proper trailing delimiter. You should gladly accept both. Of course, it is up to your internal workings to fix things up properly, but your code should be forgiving and take both “c:\mypath” and “c:\mypath” with equal grace.

Your classes might provide overloads for input methods, accepting both an integer and a string as input, providing a way for your class to be as forgiving as possible. It may be that your class has an ID property of type integer, but the caller of your application holds that ID as a string value. You should, of course, accept the string value as long as you can convert it into a valid value for your application.

On the other hand, when you call another API, your code should be strict and always send data in the correct, expected form. You should trim your string names before they get sent along to the API you are calling. If the system expects integers, you are going to send integers meeting the specification completely.

(22)

Another example is the use of nil. Your code should never passnil to a method that you are calling and

always provide a valid instance if the API asks for one. Also, as discussed above, your code should accept ‘nil’, but “fail fast” and immediately raise an exception.

Okay, now let me be clear: I’m not arguing that your code should accept anything. I’m arguing that your code should determine what is acceptable, and if the input sent by the caller can be made acceptable, then you should accept it. It should be as hard as possible to use your API incorrectly.

Thoughts on Composition over Inheritance

Of the three OOP principles, inheritance was probably the second principle that you came to understand after encapsulation. It’s a pretty basic idea – you can augment an existing class while still using all the capabilities of the parent class. Easy as pie, right?

Interestingly, inheritance has decidedly fallen out of favor recently, giving way instead to the notion of composition. Inheritance is a powerful feature, but it can get out of control. Let’s look at an example to illustrate the point.

Pretend you own a pizza shop. You want to write a system to manage all your pizzas. So you start out with a base class:

TPizza = abstract class;

Now, of course, that’s a nice abstract class, so we have to descend from it. All pizzas have sauce, so we’ll create:

TTomatoSaucePizza = class(TPizza);

And of course, we’ll need cheese:

TTomatoSauceCheesePizza = class(TTomatoSaucePizza);

That’s great. But your customers will want more ingredients. Let’s start with Pepperoni and Mushrooms. We’ll end up with:

TTomatoSauceCheesePepperoniPizza = class(TTomatoSauceCheesePizza); TTomatoSauceCheeseMushroomPizza = class(TTomatoSauceCheesePizza);

and of course, your customers may want both, so you’ll need:

(23)

Now lets add in sausage, onions, anchovies and black olives.

Hmmmm. This is going to get complicated really fast, isn’t it? Using inheritance, you are going to get a rather deep and wide inheritance model really quickly. Your properties aren’t going to cover all the possible situations that happen as more ingredients are added. The overall design quickly becomes unwieldy and inflexible. You’ll have a lot of code to write when the boss decides to add pineapple to the pizza, setting aside the fact that pineapple on pizza is an abomination.

Inheritance is cool and everything, but it has its problems.

• It encourages a gaggle of unwieldy subclasses. You can end up with many, many classes that may or may not meet your needs.

• You can never be quite sure what the super-class is going to do. Perhaps it is in a module outside of your control, and is very heavy with many dependencies. Creating it can cause side-effects that you don’t want or aren’t prepared for.

• Class hierarchies are very hard to change once they’ve been deployed. You might be stuck with pizzas that no one ever orders, and creating a new, special pizza on the fly will be impossible.

• You can’t change a super-class without risking breaking a user’s sub-class. But there is a better way. How about we “compose” a pizza instead:

type TPizza = class Cheese: TCheeseType Sauce: TSauceType Ingredients: IList<TTopping>; end;

This is a perfect example of why you should prefer composition over inheritance. The composition version of our pizza is simple. It is more flexible, allowing for multiple cheese types, sauce types, and a practically infinite collection of ingredients. The above example – a very simple one – could easily have private fields, public properties, and a constructor to allow it all to be created in one shot. Okay, let’s show that:

type TPizza = class private FCheese: TCheeseType FSauce: TSauceType FToppings: IList<TTopping>; public

constructor Create(aCheese: TCheeseType; aSauce: TSauceType; aIngredients: IList<TIngredient>);

property Cheese: TCheeseType read FCheese;

property Sauce: TSauceType read FSauce;

property Ingredients: IEnumerable<TTopping> read FToppings;

end;

You’d be hard pressed to describe a pizza that this single class can’t encompass. If you need to expand the definition of what a pizza is, you can do so easily without affecting existing uses of the TPizza class. You can

(24)

replace the entire earlier hierarchy with one simple class. Composition allows you to do in one class what might take 2ˆn classes via inheritance. It also provides a vastly simpler, more testable code-base.

Another way to look at the issue of composition over inheritance is via interfaces (it always comes back to interfaces, doesn’t it?). In a coming chapter, we’ll discuss SOLID code. As a quick preview, the Interface Segregation principle states that you should keep your interfaces small and simple. If you do that, they can be used to compose classes using those interfaces rather than inheritance, resulting again in more flexibility.

..

Note that it is said to prefer composition over inheritance. It doesn’t say always use composition over inheritance. Inheritance has its place – it’s just not usually the best choice when designing a class framework.

For example, take the classic example of TVehicle. It’s common to create examples of using inheritance to

define vehicles, adding wheels and a steering method. Then along comes a boat, and you are screwed. Well, composition of interfaces can allow for this kind of thing.

Consider the following:

type ISteerable = interface procedure TurnLeft; procedure TurnRight; end; IWheel = interface procedure Rotate; end; IMaker = interface

function GetName: string;

end; IGo = interface procedure Accelerate; end; IStop = interface procedure Brake; end;

TCar = class(TInterfacedObject, ISteerable, IMaker, IGo, IStop) LeftFrontWheel: IWheel; LeftRearWheel: IWheel; RightFrontWheel: IWheel; RightRearWheel: IWheel; procedure TurnLeft; procedure TurnRight;

function GetName: string;

procedure Accelerate;

procedure Brake;

(25)

TBicycle = class(TInterfacedObject, ISteerable, IMaker, IGo, IStop) FrontWheel: IWheel;

RearWheel: IWheel;

procedure TurnLeft;

procedure TurnRight;

function GetName: string;

procedure Accelerate;

procedure Brake;

end;

TBoat = class(TInterfacedObject, ISteerable, IMaker, IGo)

procedure TurnLeft;

procedure TurnRight;

function GetName: string;

procedure Accelerate;

end;

Here we have a collection of interfaces that can be put together – composed, if you will – to create any kind of vehicle, even a boat. Note thatTBoatandTCararen’t all that different in their declaration, except that a

car has wheels. The various interfaces available are enough to define the functionality of almost any vehicle, and if they aren’t, it’s pretty simple just to add another one. In the case above, you might think about adding

ILightsandITrailerHitch as interfaces to be used by vehicles that can use them. Both could be added

and composed with relative ease and a lack of pain to existing users of the class framework. Composition thus becomes preferable for a number of reasons:

• Your composed class can be easily added to without repercussion. Perhaps you want to add a new type of cheese toTPizza– no problem. Perhaps you decide that you want to add specific kinds of crusts –

that can be easily added without fear of breaking existing uses ofTPizza.

• Composition allows you to delay the creation of components until they are needed, or to never create them at all if they are not needed. Perhaps your TCar is driving in the day, and the lights aren’t needed, so they never even need to be created. Instead, the notion of lights could be set up to be either “On” or “Off”, and the actual state of any object that manages the lights could be hidden behind those two states.

• You can also design your classes so that components can be dynamically changed if need be. You can’t do that with inheritance. Once you’ve created TAnchoviesPeppersSausageMozzarrelaThinCrust-Pizzayou are stuck with it. The composedTPizzacan change its crust type on the fly if need be.

Conclusion

Okay, there you have six thoughts to, well, think about.

• Encapsulation is a bit more complex than you may have thought.

• High cohesion and low coupling are critical to well designed, easily maintained systems. • Command/Query Principle will have the same effect, making your code more “reasonable”. • Postel’s Law will make your API easier to use and your code more concise when using other APIs.

(26)

• If you compose your classes instead of using inheritance, you can often create a more flexible design. All good stuff, eh?

(27)

Introduction

Who doesn’t want to write solid code?

Of course, there is solid code and then there is SOLID code. SOLID is an acronym that describes five principles that will help you write solid, clean code.

Just to get it out of the way, the five SOLID principles are: • Single Responsibility Principle (SRP)

• Open/Closed Principle (OCP) • Liskov’s Substitution Principle (LSP) • Interface Segregation Principle (ISP) • Dependency Inversion Principle (DIP) We’ll cover each one in turn, but there they are.

The SOLID principles were first compiled by Robert C. Martin, probably better known as “Uncle Bob”. You can read the original articles on Uncle Bob’s website athttp://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod¹¹. The principles are just that – principles. They are more guidelines than hard and fast rules. They can’t be contained in a single framework or code library. They can often be utilized together. For example, applying the Liskov Substitution Principle means you almost can’t help writing code that follows the Single Responsibility Principle and the Dependency Inversion Principle.

What SOLID Is

Despite the plethora of OOP based languages, many developers tend to write procedural based code. The classic example is “OnClick” programming that I’ve railed about so often. All too often we think we are creating a class framework, but we are really just wrapping up our procedural code in classes. The classic example here is a “God Class,” where you start out with a single class and keep adding things to it until it can and does do everything. You probably have seen more than one of these as aTFormdescendant.

The SOLID principles are designed to ensure that you write true, object-oriented code. They are the true basis of OOP. If you follow them, it will be very difficult to merely write procedural code in objects. They will force you to think about how your objects are designed and how they should be properly written. Their purpose is to help you write better OOP code – code that is properly decomposed and decoupled and thus highly maintainable and testable.

Follow these principles, and you won’t be able to help yourself: You’ll write better code despite yourself.

¹¹http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod 15

(28)

Now, here’s something that is going to happen if you write SOLID code: You are going to have more classes, more code,more modules,and more units than you might be used to. This is okay. For some reason, developers shy away from “lots of classes,” preferring fewer classes that do more things. Is there a better definition of “heavily coupled code” than “fewer classes that do more things”? More classes doing one thing each (Single Responsibility Principle) is the path to clean code.

I’ve said it before and I’ll say it again here: There is no end to the mischief that occurs in code when a developer tries to make one thing do more than one thing. So don’t be afraid of decomposed, decoupled code even if it results in lots of classes, interfaces, and units.

SOLID code will also answer the problem of many of the Design Smells that plague code.

• Code that is difficult to change because it is tightly coupled and written procedurally is said to be rigid. The SOLID principles will make your code less rigid.

• Code that is easy to break because it is poorly organized with connections that cross all kinds of boundaries is said to be fragile. The SOLID Principles will make your code less fragile.

• Sometimes we write code that is immobile – that is, it is hard to reuse in new places. The SOLID principles will make your code mobile and reusable.

• Viscosity is the measure of your code’s ability to make it easy to do the right thing. Following the SOLID principles will make your code more viscous – allowing you to do the right thing when you know you should do it, instead of doing the wrong thing when you know you shouldn’t.

• Finally, our code can become over-designed when we give it needless complexity. SOLID code is simple and clean and will help you prevent over-design.

Okay, so let’s dive right into it.

Single Responsibility Principle

The Single Responsibility Principle states that a class should do one and only one thing. Uncle Bob says that it means “a class should have only one reason to change.” However you look at it, a class should not be doing multiple things. It should be doing one thing. One and only one. Not two, not three – one.

Consider the following class declaration:

type TBook = class private FCurrentPage: integer; FTitle: string; FAuthor: string;

procedure SetTitle(const Value: string);

procedure SetAuthor(const Value: string);

public

procedure DisplayPage; // Book Stuff

function TurnPage: integer; // Book stuff

procedure PrintCurrentPage; // Prints itself

procedure SaveCurrentPage; // Saves itself

property Title: string read FTitle write SetTitle;

property Author: string read FAuthor write SetAuthor;

(29)

This class probably resembles a lot of classes that you may have written or seen. And it all seems reasonable, right? A book does a bunch of things: it should display its pages, turn pages, print things, etc. What’s not to like here?

Well, what’s not to like is that if you want to change the way the class prints, you have to change the class itself. And maybe someone else is using the class and likes the way that the printing part works. The class is coupled to printing and displaying and saving and who knows what else.

In other words, this class is a pretty gross violation of the Single Responsibility Principle. So what is the solution? How about this:

type

IPrintable = interface

procedure Print(aString: string);

end;

TConsolePrinter = class(TInterfacedObject, IPrintable)

procedure Print(aString: string);

end;

IPageNumberSaver = interface

procedure Save(aPageNumber: integer);

end; type TBookManager = class private FCurrentPage: integer; FTitle: string; FAuthor: string;

procedure SetTitle(const Value: string);

procedure SetAuthor(const Value: string);

public

function TurnPage: integer;

procedure PrintCurrentPage(aPage: IPrintable);

procedure SavePageNumber(aPageNumberSaver: IPageNumberSaver);

property Title: string read FTitle write SetTitle;

property Author: string read FAuthor write SetAuthor;

end;

ThisTBookdoesn’t really need to change. We’ve used dependency inversion (DIP) to remove the functionality

of printing and saving. If you don’t want to print to the provided console printer, pass in a different implementation of IPrintable. Want to save your page number to anINIfile or a database? Pass in the

appropriateIPageNumberSaverimplementation.TBookknows nothing about the inner workings of those

implementations and thus has no reason to change because of them.

TBookdoes one thing now; it is a book and nothing more. It’s not a printer thing or a saver thing. You’d only

change it if you want to change the way that it behaves as a book. It turns over the responsibility for printing and saving to external interfaces. No need to change anything if you want to print a different way – just pass in a differentIPrintableimplementation.

(30)

Open/Closed Principle

The Open/Closed Principle states that a class should be open for extension but closed to change. That is, a properly defined class, once deployed, shouldn’t ever need to be changed (except for bug fixes, of course) and if there is new functionality that needs to be added, it should be added through extension or inheritance. We are all guilty of violating this one. Shoot, if you have used a case statement in your code, it is very possible that you are violating the Open/Closed Principle. Take a look at this code

type

TItemType = (Normal, TenPercentOff, BOGO, TwentyPercentOff); TItem = record SKU: string; Quantity: integer; Price: double; ItemType: TItemType; end; TOrder = class private FListOfItems: IList<TItem>;

function GetItems: IEnumerable<TItem>;

public

procedure Add(aItem: TItem);

function TotalAmount: Double;

property Items: IEnumerable<TItem> read GetItems;

end;

implementation

{ TOrder }

procedure TOrder.Add(aItem: TItem);

begin

FListOfItems.Add(aItem);

end;

function TOrder.GetItems: IEnumerable<TItem>;

begin

Result := FListOfItems;

end;

function TOrder.TotalAmount: Double;

var

Item: TItem; LQuantity: integer;

begin

for Item in FListOfItems do begin

case Item.ItemType of

Normal: begin

Result := (Item.Price * Item.Quantity);

(31)

TenPercentOff: begin

Result :=(Item.Price * 0.90 * Item.Quantity);

end;

BOGO: begin

LQuantity := Item.Quantity * 2; Result := Item.Price * LQuantity;

end;

// More things coming!

end;

end;

end;

This code is wide open for change. If you want to add a new sale type, you have to dig down into the case statement of the TotalAmountfunction in order to complete that change. The class is most definitely not

closed to change.

For instance, if you wanted to add a new TwentyPercentOff item, you’d have to add an item to the

enumerated type (not that big a deal), but you’d have to add another item to the case statement in the

TotalAmountfunction. This isn’t the way you want to do things, as altering the class itself makes it easy

to introduce bugs, causes you to have to update your test cases, and generally is fraught with peril. Why not design things so that you can deploy the class once, and then extend it easily instead of having to crack it open and change it?

Here’s an example of using an interface (ISP) and dependency inversion (DIP) (which we’ll look at below) to do the same thing in a way that makes it easy to extend:

type IItemPricer = interface; TItem = record ID: string; Price: double; Quantity: integer; Rule: IItemPricer;

constructor Create(aID: string; aPrice: Double; aQuantity: integer; aRule: IItemPricer);

end;

IItemPricer = interface

function CalculatePrice(aItem: TItem): Double;

end;

TNormalPricer = class(TInterfacedObject, IItemPricer)

function CalculatePrice(aItem: TItem): Double;

end;

TTenPercentOffPricer = class(TInterfacedObject, IItemPricer)

function CalculatePrice(aItem: TItem): Double;

end;

TBOGOFreePricer = class(TInterfacedObject, IItemPricer)

(32)

end;

TTwentyPercentOffPricer = class(TInterfacedObject, IItemPricer)

function CalculatePrice(aItem: TItem): Double;

end;

TOrder = class private

FListOfItems: IList<TItem>;

function GetListOfItems: IEnumerable<TItem>;

public

procedure Add(aItem: TItem);

function TotalAmount: Double;

property Items: IEnumerable<TItem> read GetListOfItems;

constructor Create;

end;

(I’ve shown just the interface declaration here as the implementation should be obvious and straightforward. You can find the implementation on BitBucket if you want to look more closely at how it all works.)

First,we declare theIItemPricerinterface which will handle the task of doing special pricing for a given item

in our inventory. Then,TItemtakes anIItemPriceras a parameter on its constructor. Next, we implement IItemPricermultiple times in separate classes with the various pricing schemes that we want to deploy.

Once we have all those implementations ofIItemPricer, we can add them to an item in theconstructor

with ease. We can also create new ones without having to change theTOrderclass at all. It’s ready to take

on any number of items, and those items are ready to take on any pricing rules. Thus,TOrderis closed for

change – there’s really no reason to alter it – and open for extension – you can easily create new pricing rules and add them to the items list ofTOrder.

Sure, we have more classes, but each is super easy to test, extending the system isn’t difficult at all, and the base class,TOrder, need not be changed at all even if we want to add a new way to discount items.

So there you have a nice, clean example of being open for extension but closed for change – the Open/Closed Principle.

Liskov’s Substitution Principle

Liskov’s Substitution Principle is named after Barbara Liskov, the person who identified it. Liskov stated it quite formally:

“Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects

y of type S where S is a subtype of T.”

Now, it took me a while to decipher that. Actually, I never did quite decipher it without some help. Uncle Bob put it less formally: “Subtypes must be substitutable for their base types.” Or as Mark Seemann puts it: “A client should be able to consume any implementation of an interface without changing the correctness of the system.” Basically, it means that you should design your system so that either a child class or any implementation of an interface can be substituted for either the base type or that given interface without things being screwed up.

(33)

An example will be necessary here, I think. How about the classicTVehicleexample? That is always prime

for problems:

TVehicle = class abstract

procedure Go; virtual; abstract;

procedure FillWithGas; virtual; abstract end;

Okay, if you have a keen eye, you’ll already notice a problem here, but roll with me.TVehicle has two

abstract methods. Any descendant will have to implement both of those. That’s great – here’sTCar:

TCar = class(TVehicle )

procedure Go; override;

procedure FillWithGas; override;

end;

That works great. The implementations of the methods will be what you’d expect, and a TCar can

polymorphically be used as aTVehicle. All is well.

But of course, what happens when you try to defineTBicycle:

TBicycle = class(TVehicle)

procedure Go; override;

procedure FillWithGas; override;

end;

TheGomethod looks great:

procedure TBicycle.Go;

begin

WriteLn('Pedal like crazy');

end;

But what to do about gas? Hmmm.

procedure TBicycle.FillWithGas;

begin

raise EVehicleException.Create('You silly, you can''t put gas in a bicycle.');

end;

Okay, so here’s your first clue that you are violating Liskov’s Substitution Principle: in order to implement a class, you basically have to raise an exception or otherwise not implement a given method of a descendant class. If your descendant class can’t properly implement something it needs to implement, your base class is violating the Liskov Substitution Principle.

(34)

type

TVehicle = class abstract

procedure Go; virtual; abstract;

end;

TGasVehicle = class abstract(TVehicle)

procedure FillWithGas; virtual; abstract;

end;

TCar = class(TGasVehicle )

procedure Go; override;

procedure FillWithGas; override;

end;

TTruck = class(TGasVehicle)

procedure Go; override;

procedure FillWithGas; override;

end;

TBicycle = class(TVehicle)

procedure Go; override;

end;

Here, we’ve removed the gas requirement fromTVehicle and put it in its own classTGasVehicle. That

leavesTBicycleclear to only worry about theGomethod and not worry about gassing up.

The Liskov’s Substitution Principle also works with interfaces. Let’s take a look. Here’s an interface for a bird: IBird = interface

procedure Fly;

procedure Eat;

end;

Birds fly and eat, right? Perfect interface. (Hehe…..) Here’s an implementation:

TCrow = class(TInterfacedObject, IBird)

procedure Fly;

procedure Eat;

end;

Now, let’s implementTPenguin:

TPenguin = class(TInterfacedObject, IBird)

procedure Fly;

procedure Eat;

end;

Uh oh. What do we do about implementing theFlymethod? Penguins are great swimmers but they can’t fly

(35)

IFlyable = interface procedure Fly; end; IEater = interface procedure Eat; end;

TCrow = class(TInterfacedObject, IFlyable, IEater)

procedure Fly;

procedure Eat;

end;

TPenguin = class(TInterfacedObject, IEater)

procedure Eat;

end;

Our firstTPenguinviolated the Liskov Substitution Principle, but when we split up the interfaces, we can

apply them as needed and all is well. And here’s the fun part – remember when I said that these principles often work together? As we’ll see, the above solution used the Interface Segregation Principle to solve the dilemma posed byIBirdandTPenguin.

Here’s a list of things that might make you think that you are violating the Liskov’s Substitution Principle: • Your code throws aNotSupportedExceptionor something similar when implementing a method.

• If you are doing a lot of typecasting to get a class to be the right class in all cases.

• Extracted interfaces that aren’t properly segregated will often result in an Liskov’s Substitution Principle violation

• Often times, you might find yourself having to remove features when implementing an interface. This might be an indication that you are violating Liskov’s Substitution Principle. One example would be a read-only collection, when you would need to remove things likeAddandInsertto make it all work.

Interface Segregation Principle

The Interface Segregation Principle states that “Clients should not be forced to depend on methods that they do not use.” That is, the client should be the one to define what it needs and not be forced to depend on things that it doesn’t want to or need to depend on. An interface is defined by the client and not the other way around.

Do you feel like committing a gross violation of the ISP? Grab one of your classes, copy its public section, and turn that into an interface. I’ll almost 100% guarantee that doing that will violate the ISP. Think about it – by doing that, you are having the implementor, and not the client, define what the interface is.

In order to ensure that you follow the ISP, you should favor writing role-based interfaces over header-based interfaces. We saw this in the Liskov’s Substitution Principle section previously: Rather than have a single

IBirdwith aFlyand anEatmethod, you should instead let the client dictate things – in this caseTCrow

References

Related documents

The guidance gives practical advice on the legal requirements of the Health and Safety at Work etc Act 1974, the Control of Substances Hazardous to Health Regulations 2002

According to the new regulations the water supplier is required to submit an Emergency Response and Contingency Plan (ERCP). An ERCP is a written document that spells out a

Private investors, called to lend to short-term funds to banks during the crisis, are assumed to have only imperfect information about the true exposure of a given bank..

The third carbon has to be attached to either of the other two carbons forming a three carbon chain with eight additional bonding sites, each bonded to a hydrogen atom.. Propane is

Bridges (Folk, Classic, &amp; Archtop) Bridges (Folk, Classic, &amp; Archtop) Carbon Rods Carbon Rods Center Strips Center Strips Conversion Bushings Conversion Bushings Extension

In this paper, we review some of the main properties of evenly convex sets and evenly quasiconvex functions, provide further characterizations of evenly convex sets, and present

70282 with MedQuist, effective upon Board approval, to extend the Agreement term for the period January 1, 2011 through March 31, 2011 for continued services at Olive

Delegate authority to the Director of Health Services (Director), or his designee, to execute a Model eHealth Equipment Loan and Service Agreement with Los Angeles Care Health