Practical .NET

Leveraging Interfaces Instead of Inheritance

If you need to have objects look alike but don't have any code to share, you don't need inheritance -- you need an interface. Here's an example of how interfaces provide a more flexible way to deal with similar-but-different classes.

You have a set of classes that you want to use interchangeably but that hold different code. Most developers reach for the obvious tool: inheritance. But what if you don't have any common code to put in your base class? In that scenario, inheritance is overkill. Alternatively, you might have a diverse set of classes that you want to work with -- creating the right inheritance tree for all of those classes could be difficult.

Interfaces are the better answer. I've been working with a case study where I've been developing value types using structs, rather than classes. Structs don't even support inheritance so I have no choice but to use interfaces if I want to do anything interesting. However, even if I was working with classes, interfaces would be my best choice.

Designing Interfaces
In my case study, I have a set of Adjustment objects that reflect discounts that can be applied to a sales order. Some adjustments are absolute ("$5.00 off for paying in cash!") and some are percentage based ("5% off for buying two or more!").

Obviously, I need two different kinds of Adjustments: an AbsoluteAdjustment and a PercentageAdjustment so that I can keep the two types of changes separate. For example, I shouldn't mix AbsoluteAdjustments and PercentageAdjustments when sorting adjustments (is a 5 percent PercentageAdjustment greater or less than a $5.00 AbsoluteAdjustment -- I can't tell without applying it to a sales order). Equally obvious, there are going to be times when I want to be able to process all of the Adjustments for a SalesOrder, regardless of what kind of Adjustment it is.

To support the "process all adjustments" scenario, I define an interface that all Adjustments will share. All Adjustments have a Name property ("PayingCash," "MultiplePurchases") and a FormattedAmount that returns an appropriate string for displaying the Adjustment. Here's what that interface looks like:

Public Interface IAdjustment
  Property ReadOnly Name As String
  Property ReadOnly FormattedAmount As String
End Interface

Now I design the interfaces for when I need to keep AbsoluteAdjustments (like PayingCash) and PercentageAdjustments (like MultiplePurchases) separate. Both have Decimal properties, but the names of the properties are different. While I can't use inheritance when defining structs, I can use inheritance to extend my IAdjustment interface into two new interfaces, IAbsoluteAdjustment and IPercentageAdjustment, like so:

Public Interface IAbsoluteAdjustment
  Inherits IAdjustment
  Property ReadOnly Amount As Decimal
End Interface

Public Interface IPercentageAdjustment
  Inherits IAdjustment
  Property ReadOnly Percentage As Decimal
End Interface

Leveraging Interfaces
With my interfaces in place, I can use them when defining new classes. Here's the start of my PayingCashAdjustment struct:

Public Structure PayingCashAdjustment
  Implements IAbsoluteAdjustment

  Private _Name As String
  Private _Amount As Decimal

  Friend Sub New(Name As String, Amount As Decimal)
    _Name = Name
    _Amount = Amount
  End Sub

Listing 1 shows the implementation of the three properties required by the IAbsoluteAdjustment interface and its base.

Listing 1: Implementing an Interface
Public ReadOnly Property Name As String Implements IAbsoluteAdjustment.Name
  Get
    Return _Name
  End Get
End Property

Public ReadOnly Property Amount As Decimal Implements IAbsoluteAdjustment.Amount
  Get
    Return _Amount
  End Get
End Property

Public ReadOnly Property FormattedAmount As String Implements IAbsoluteAdjustment.FormattedAmount
  Get
    Return FormatCurrency(_Amount)
  End Get
End Property

A MultiplePurchases Adjustment would look very similar, but would use the IPercentageAdjustment interface. The IPercentageAdjustment struct would have very different code in its FormattedAmount method to display the percentage used with a percentage adjustment.

Using the Interfaces
I can now use the IAdjustment interface to declare a variable to loop through all of the Adjustments for a SalesOrder:

Dim adjusts As List(of IAdjustment)
adjusts = AdjustmentFactory.GetAdjustmentsForASalesOrder("B456")
ForEach adj as IAdjustment in adjusts

When I want to use one of the two more specialized interfaces (IAbsoluteAdjustment and IPercentageAdjustment), I can check to see which interface an Adjustment is implementing:

ForEach adj as IAdjustment in adjusts
  If adj Is IAbsoluteAdjustment Then
    '...code for working with absolute adjustment
  End If

Here's another benefit of using interfaces: Let's say you have some other class that you want to use as an Adjustment. To do that, you just have your class implement one of the two interfaces (IAbsoluteAdjustment or IPercentageAdjustment) and put some code in the interface's methods. Typically, the code you put in the interface's methods will call other code that already exists in the class. The beauty of this process is that this new Adjustment class doesn't have to change its inheritance structure in order to become an "Adjustment-compatible" class.

I have nothing against inheritance … but it's not the only tool in your toolbox. If you don't have code to share and you need some additional flexibility, you should consider using interfaces first.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Full Stack Hands-On Development with .NET

    In the fast-paced realm of modern software development, proficiency across a full stack of technologies is not just beneficial, it's essential. Microsoft has an entire stack of open source development components in its .NET platform (formerly known as .NET Core) that can be used to build an end-to-end set of applications.

  • .NET-Centric Uno Platform Debuts 'Single Project' for 9 Targets

    "We've reduced the complexity of project files and eliminated the need for explicit NuGet package references, separate project libraries, or 'shared' projects."

  • Creating Reactive Applications in .NET

    In modern applications, data is being retrieved in asynchronous, real-time streams, as traditional pull requests where the clients asks for data from the server are becoming a thing of the past.

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

Subscribe on YouTube