in

ExpressionBlog.com

Microsoft Expression Studio Community

This Blog

Syndication

Mirrored Blogs

Browse by Tags

All Tags » oo (RSS)
  • Why SOLID? GIMME AN L!

    .csharp { font-family: 'Consolas', 'Courier New'; color: #33F;} Thus far in my journey to explain the why of the so-called SOLID principles I've covered Single Responsibility Principle and Open/Closed Principle . This brings us to "L" for Liskov Substitution Principle which the originator, Barbara Liskov , describes as: What is wanted here is something like the following substitution property: If for each object o 1 of type S there is an object o 2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o 1 is substituted for o 2 then S is a subtype of T. Whoa! Math meets English and kicks its... well, you know. Robert Martin boils it down a bit: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. If we have a consuming function that takes a Customer class, it shouldn't matter if that gets a GoldCustomer specialization. We don't want, in our consumer code, to rely on the is or as keywords to branch our consumer accordingly. // For shame! void Consumer(Customer customer) { if (customer is GoldCustomer) { var goldCustomer = customer as GoldCustomer; goldCustomer.DoSomethingElse(); } else { customer.DoSomething(); } } Seems sensible, but recall: we're dealing with the why and operating under the strict belief that it's unacceptable to invoke these principles without understanding their deeper reasons and benefits . It's particularly important that we be able and ready to articulate the reasons for these tried truisms as we mentor new developers and win entrenched developers over to known better ways of doing things. Mind Your Coupling A lot of these principles come down to coupling and cohesion. LSP is all about controlling coupling . Notice how in the code example we've touched on two types? Customer and GoldCustomer are both used by our (rather creatively named) Consumer method. So what if we add a PlatinumCustomer ? We've created a permalink between our consumer and a wider-than-desired surface area of our customer model. Respect the OCP As Robert Martin points out , ignoring LSP is a clear violation of "the Open-Closed principle because [that function] must be modified whenever a new derivative of the base class is created." So LSP and OCP are intrinsically linked, and, if you remember , the why behind OCP has to do with stability and moving forward by only ever adding new behavior. If I have to change the Consumer function every time I create a specialization of Customer is that function closed to modification? Clearly not. Fragile APIs Ignoring this has particularly nasty effects when we're writing code or an API to be consumed by someone else. By "someone else" I mean either: Another team in the same timeline. Another developer that has to come along and maintain our code. Ourselves way down the timeline; we'd have to remember this bit of code exists...
  • TDD, Contracts, and Reading the Fine Print

    Jimmy Bogard on coming to TDD and interface oriented Jesus and the aha moment of why we use interfaces: An interface is a contract, that any implementation needs to adhere to. Consumers of the interface do not care about implementation details, nor can they, as the interface provides no other information besides the signatures it provides. I quite like the point that Jimmy hints at. Classes, at a certain level, are tiny components. But is an interface really a contract? Yes... but it's not the whole contract ; our tests and specifications are part of the contract of our components. A component does a couple of things. It will a) tell its container environment what services it needs to run and b) what services it provides. It does this by requiring or publishing one or more contracts. As far as what we have right now in the mainstream, if we're pursuing the highest quality, it's up to us to deliver contracts that fully document not just the data (as interfaces do) but, at times, the behavior or semantic qualities of these tiny components we're writing. Sometimes those behaviors mean something to the supplier and the consumer. Say we have a controller and that controller depends on a service. The controller creates a transaction and invokes a service method. Perhaps that service method then throws a business-meaningful exception. For argument let's say we have a shopping cart and you've just added -1 pairs of agile pants to your order. Wait a second... what just happened?! The fact the exception happened is surely a part of the contract! We have to deal with it after all and abort the transaction or take some compensating action. In the case of ordering some agile pants, maybe you want to display a message indicating that quantity should be one or more. Yes, I realize this is a bad example, but stuff like this necessarily happens in the real world. What if -- just go with me here -- we have a dependency that can throw an exception? Is that not part of the contract? Last I checked there wasn't a way to say "hey, this interface can throw these kinds of exceptions" in C#. Consumers surely need to care about implementation details. What about invariants? If we simply have a structural contract we have nothing that expresses these behavioral elements. C# might get some spec# features, but we're not quite there yet. Preconditions, postconditions, and invariants are a part of the contract we take in. When we're driving out interfaces and using a nice mock objects framework, we need to think about our consumers when we dive into a dependency and drive it out. What preconditions are you creating? What exceptions are you throwing? Do consumers of this service need to be aware or are they likely to want to handle these exceptions? If so, you need to return to the consumer and write some more tests. Get more mileage out of your code by applying defensive programming conservatively . Simply put: contract = specifications...
  • Why SOLID? GIMME AN O!

    The next leg of our quest to uncover the deeper driving forces behind SOLID principles brings us to the Open/Closed Principal (OCP). To refresh, I think it's important that if we're going to say (read the next part in a mouth-breathing, war-hammer-playing, mega-nerd voice) "your design violates TLA," we had better back it up with some solid reasoning. Most blog posts focus on the what and how . On this tip there are some great resources out there already. I've even blogged about a technique for achieving OCP with template method and chain-of-responsibility. No matter; we're here to focus on the why ! Without further explanation, let's take a look at... The Open/Closed Principle OCP, as introduced by Bertrand Meyer , states: Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. Once we've decided and are satisfied with the cohesive responsibilities of our class , we want that responsibility to remain stable. Stability is a big, big deal. It allows us to keep forward motion and focus on adding new and interesting behavior in other places in our system. "The Tortoise & The Hare" fable springs to mind. Proceeding methodically and consistently by investing in the design of our software usually pays dividends in terms of the system's total cost of ownership. Jeremy Miller does a great job of linking SRP with OCP in his recent MSDN article : You'll never reach true Open Closed nirvana, but you can inch much closer by rigorously following the related Single Responsibility Principle: a class should have one, and only one, reason to change. One of the easiest ways to write classes that never have to change is to write classes that only do one thing. This way, a class only needs to be modified if the exact thing that it does needs to change. Behavior inevitably changes. We can inject "hooks" or extensibility points into our classes. This lets us keep our existing classes stable while extending their behavior. In a certain sense we could reword OCP as maintain stability in your designs by providing extension points. Sometimes we want to distribute a library and more-often-than-not our code is broken up into multiple assemblies or packages and shared between teams, customers, and other developers in general. Providing hooks that "open classes to extension" help deal with variation just as it aids internal evolution. This topic hints at the larger subject of API design. OCP in the Dynamic Paradigm It's interesting to think about how these principles in a dynamic language like Ruby, considering you can just open any class anywhere or use various techniques such as the instance_variable_set method or alias to completely modify the behavior of an existing class. Michael Feathers points out, in a comment on Dean Wampler's post about OCP in Ruby, that's something we can't do in static languages: The second problem I have...
  • Why SOLID? GIMME AN S!

    There was some chatter on twitter today about why SOLID principles are a good idea. When designing object systems we often tote around a lot of acronyms for principles of good design, but why are these things principles? It's not enough to simply say "just because." So right here and now I'd like to examine the "why" of some time tested OO aphorisms. Yes, SOLID has been covered many times in many places, but it's hard to find a single resource explaining the reasoning behind these ideas. Please spare me the "I wrote this post 18 months ago." I won't go into the "what" or "how" because I think that's been covered inside and out. We're here to talk about the principals behind the principals. Namely: what does SOLID buy you. Now that all necessary disclaimers have been, well, disclaimed, here goes! Single Responsibility Principle (SRP) SRP simply states that a class should have one and only one reason to change . There is a clear reason for this: making sure your classes embody one purpose in life makes your system more receptive to change, more stable . Once you've nailed an object's raison d'être chances are you can move on with your life. You don't need to introduce changes that can, in turn, introduce instability. From Uncle Bob Martin : If a class has more then one responsibility, then the responsibilities become coupled. Changes to one responsibility may impair or inhibit the class’ ability to meet the others. This kind of coupling leads to fragile designs that break in unexpected ways when changed. Changes can also ripple through the system as classes depending on our multi-responsibility class (which is more likely to change than a single responsibility class) have clients in other programs. It's worth considering your responsibilities and favoring more discrete classes with fewer methods. SRP is really about analyzing cohesion. If you're interested in going deeper, I recommend checking out this entry on cohesion to better understand the science behind SRP. Did you, for example, know their are different levels and forms of cohesion? Next up: the Open-Closed Principle...
Powered by Community Server (Commercial Edition), by Telligent Systems