locked
What are abstract classes for? RRS feed

  • Question

  • I'm sorry if my question is nonesense or I posted it in the wrong forum.

    I understand that abstract classes:

    • Must be inherited
    • Cannot be implemented

    But I have a simple piece of code that I just can't understand, I would appreciate if someone can help me with this. Let's suppose I have the following abstract class and the class that inherit form it:

    Public MustInherit Class AbstractClass
        Sub New()
        End Sub
        Public MustOverride Sub Hello()
    End Class
    
    Public Class drivedClass
        Inherits AbstractClass
    
        Public Overrides Sub Hello()
            ' Hello Message
            MessageBox.Show("Hello")
        End Sub
    
    End Class

    isn't just easier to have this:

    Public Class drivedClass
        Public Sub Hello()
            ' Hello Message
            MessageBox.Show("Hello")
        End Sub
    End Class

    What is the functionality or the advantage of using an abstract class?, I'm totally lost.

    Thanks in advance.


    G.Waters

    Sunday, June 16, 2013 10:39 AM

Answers

  • VB.Net represents a strongly-typed, object-oriented language and as such utilizes "code contracts" to define how objects and their derivatives should behave.

    The fundamental code contract is the Interface.  An Interface defines the core structure of an object, but does not define any actual functionality or member visibility.  Implementers are free to make decisions about if and how each interface member is implemented in their own classes.

    An Abstract Class might be considered a tier-two code contract.  Like an Interface, the Abstract Class will define the core structure of an object, however, it may also define some common functionality and it absolutely defines the member visibility of any derivatives.

    What this means for you as the developer is that you have two subtlety different ways to define a code contract depending on the amount of flexibility you want to offer to derived classes.  Consider the following examples.  Here we will define a code contract for an "Animal" class (the animal kingdom is often a good basis for OOP and Inheritance examples as it follows a relatable pattern and is widely known) and then create two derived classes, Horse and Snake, to demonstrate some of the key differences between using an Interface and using an Abstract Class.

    Namespace InterfaceExample
        Public Interface IAnimal
            ReadOnly Property NumberOfLegs As Integer
            ReadOnly Property SkinCover As SkinCovering
            ReadOnly Property SoundItMakes As String
            ReadOnly Property Kind As AnimalKind
        End Interface
    
        Public Class Horse
            Implements IAnimal
    
            Public ReadOnly Property NumberOfLegs As Integer Implements IAnimal.NumberOfLegs
                Get
                    Return 4
                End Get
            End Property
    
            Public ReadOnly Property SkinCover As SkinCovering Implements IAnimal.SkinCover
                Get
                    Return SkinCovering.Hair
                End Get
            End Property
    
            Public ReadOnly Property SoundItMakes As String Implements IAnimal.SoundItMakes
                Get
                    Return "Neigh"
                End Get
            End Property
    
            Public ReadOnly Property Kind As AnimalKind Implements IAnimal.Kind
                Get
                    Return AnimalKind.Mammal
                End Get
            End Property
        End Class
    
        Public Class Snake
            Implements IAnimal
    
            Protected ReadOnly Property NumberOfLegs As Integer Implements IAnimal.NumberOfLegs
                Get
                    Throw New NotImplementedException()
                End Get
            End Property
    
            Public ReadOnly Property SkinCover As SkinCovering Implements IAnimal.SkinCover
                Get
                    Return SkinCovering.Scales
                End Get
            End Property
    
            Public ReadOnly Property SoundItMakes As String Implements IAnimal.SoundItMakes
                Get
                    Return "Hiss"
                End Get
            End Property
    
            Public ReadOnly Property Kind As AnimalKind Implements IAnimal.Kind
                Get
                    Return AnimalKind.Reptile
                End Get
            End Property
        End Class
    
        Public Enum SkinCovering
            None
            Scales
            Feathers
            Hair
        End Enum
    
        Public Enum AnimalKind
            Other
            Reptile
            Bird
            Mammal
        End Enum
    End Namespace
    
    Namespace AbstractExample
        Public MustInherit Class Animal
            MustOverride ReadOnly Property NumberOfLegs As Integer
            MustOverride ReadOnly Property SkinCover As SkinCovering
            MustOverride ReadOnly Property SoundItMakes As String
    
            Public ReadOnly Property Kind As AnimalKind
                Get
                    Select Case SkinCover
                        Case SkinCovering.Scales
                            Return AnimalKind.Reptile
                        Case SkinCovering.Feathers
                            Return AnimalKind.Bird
                        Case SkinCovering.Hair
                            Return AnimalKind.Mammal
                        Case Else
                            Return AnimalKind.Other
                    End Select
                End Get
            End Property
        End Class
    
        Public Class Horse
            Inherits Animal
    
            Public Overrides ReadOnly Property NumberOfLegs As Integer
                Get
                    Return 4
                End Get
            End Property
    
            Public Overrides ReadOnly Property SkinCover As SkinCovering
                Get
                    Return SkinCovering.Hair
                End Get
            End Property
    
            Public Overrides ReadOnly Property SoundItMakes As String
                Get
                    Return "Neigh"
                End Get
            End Property
        End Class
    
        Public Class Snake
            Inherits Animal
    
            Public Overrides ReadOnly Property NumberOfLegs As Integer
                Get
                    Return 0
                End Get
            End Property
    
            Public Overrides ReadOnly Property SkinCover As SkinCovering
                Get
                    Return SkinCovering.Scales
                End Get
            End Property
    
            Public Overrides ReadOnly Property SoundItMakes As String
                Get
                    Return "Hiss"
                End Get
            End Property
        End Class
    
        Public Enum SkinCovering
            None
            Scales
            Feathers
            Hair
        End Enum
    
        Public Enum AnimalKind
            Other
            Reptile
            Bird
            Mammal
        End Enum
    End Namespace

    As you can see, on the surface, each of these examples does the same thing.  They each provide a code contract for an Animal with the same object members defined. However, there are two major differences in the Horse and Snake classes that derive from Animal between the two examples.

    In the Interface example, we see that the Snake class is free to hide the NumberOfLegs property by changing the member access level.  It can then throw a standard not-implemented exception if that member should be accessed through other means such as reflection.  We also see that both the Horse and Snake class must provide their own implementation of the Kind property to return the respective AnimalKind value.

    In the Abstract Class example the Snake class must implement the NumberOfLegs property and return zero because the parent class has defined it as Public and Abstract Classes cannot change a member's access level (ignoring Shadowing, which breaks strongly typed OOP principle).

    The other difference is that the abstract Animal class itself implements the Kind property for all derived classes because the Abstract Class can provide its own member implementations where an Interface cannot.

    There are other differences between an Interface and an Abstract Class as well, such as the fact that an Abstract Class absolutely defines the lowest-level base class for an object where-as an Interface does not.  For example, a Truck class which inherits from a Machine class could still implement the IAnimal interface and become both a Machine/Truck and an IAnimal, where as a class which inherits from the Animal Abstract Class can never also be a Machine/Truck.

    You gave an example of a "Reports" class in abstract form such that each implementation was able (had to) provide its own logic for generating a report.  You then showed a modification that did away with the abstract base class.  The key difference here is that in the second example, the report generating routine is the same for both derived classes.  If you wanted to later create a third derived class, you would have to edit and recompile the base Reports class to handle the new report generation.  You no longer have a code contract for report generation.  If you stick with the first example using an Abstract Class, then the third derived class would define its own report generation without any modification of the base class (so the new derived class could even be defined in another assembly which references the assembly containing your base class).  In this way you provide a code contract for report generation without limiting the possible number of reports.

    So in summary, an Abstract Class is another type of code contract that a developer can use to provide an initial implementation of core functionality along with a stricter inheritance rule-set for an object which requires additional undefined implementation before it can be used.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Sunday, June 16, 2013 4:27 PM
  • Would it be too much to ask if you could apply this quote to my report sample?


    This example uses the fact that all the reporting classes are derived from the abstract Reports class, so that the instance can be passed as a type Reports to another method (in this case, a prrinting method).

    Depending on what the ReportPrint method needs to do, it might not even convert the instance to the correct type for printing - in that case the addition of a new report type would not require any change to the ReportPrint code.

        Private Sub CreateReport()    
            Select Case ReportType
                Case Invoicing
                    Dim IR As New InvoicingReports
                    'Create an incvoicing report as IR
                    ReportPrint(IR)
    
                Case Accounting
                    Dim AR As New AccountingReports
                    'Create an accounting report as AR
                    ReportPrint(AR)
    
            End Select
    
        End Sub
    
        Public Sub ReportPrint(ByVal Rep As Reports)
            'Print the report
        End Sub
    • Marked as answer by George Waters Sunday, June 16, 2013 10:38 PM
    Sunday, June 16, 2013 10:28 PM

All replies

  • In my opinion, if you make a subroutine that takes a derivedClass object, like

    Sub MySub(z As derivedClass)
            z.Hello()
    End Sub

    then you will not be able to pass other objects that also contain Hello. You will have to write other procedures for each case. By using abstract base class and a procedure like:

    Sub MySub(z As AbstractClass)
            z.Hello()
    End Sub

    you can pass any derived class.

    Compare for example with Stream abstract class. If you write a procedure that writes something to a Stream, then you can use it for writing to disk (FileStream) or to memory (MemoryStream), or you can compress the data (DeflateStream). These classes are derived from abstract Stream base.

    Sunday, June 16, 2013 11:13 AM
  • In that case there would be bo benefit.

    The abstract class is useful where there is a range of classes that will derive from it, but each of those classes must provide some essential functionality that makes the class usable.

    It is a means of providing a 'common core' of functionality that will be used in the derived classes, but is not itself sufficient to create a functioning, usable class.  MustOverirde is often used in an abstract class to enforce a particular pattern that the derived classes will follow, even though the implementation will be specific to each derived class.

    http://msdn.microsoft.com/en-us/library/k535acbf(v=vs.71).aspx

    Sunday, June 16, 2013 11:24 AM
  • Thanks Viorel,

    So that's it?, an abstract class only works as container of methods of the inherited class that the user can use?, please correct me, but in both of my cases the method would be called like this:

    Dim dC as new drivedClass
    dc.Hello


    G.Waters

    Sunday, June 16, 2013 11:34 AM
  • Thanks Acamar.

    Looks like I'm understanding, let's see, if an abstract class is used as a common core, would this example be right?

    Public MustInherit Class Reports
        Public MustOverride Sub Generate(ByVal Query As String)
    End Class
    
    Public Class InvoicingReports
        Inherits Reports
        Public Overrides Sub Generate(ByVal Query As String)
            'Code to make the report
        End Sub
    End Class
    
    Public Class AccountingReports
        Inherits Reports
        Public Overrides Sub Generate(ByVal Query As String)
            'Code to make the report
        End Sub
    End Class


    G.Waters

    Sunday, June 16, 2013 11:45 AM
  • Looks like I'm understanding, let's see, if an abstract class is used as a common core, would this example be right?

    Your Reports class could be implemented as a single class that was able to create any type of report, and simply needed the report type to be set in a property.

    But it could also be implemented as an abstract class that included a certain core of methods and properties associated with reporting, but marked as MustInherit so that a specific reporting class was created by inheriting from the Reports class and adding what was needed for that report type.

    In the first case, a new report type would require a modification to the Reports class.  In the second case, it would require a new derived class.

    In that particular example there is no functionality in the abstract class, so the only thing it is doing is providing a form of template for derived classes to follow, by implementing the MustOveride methods.   It would be more common for the abstract class to implement the functionality that was common to all derived classes.

    Note that a particular advantage of an abstract class is that it can be used as the Type for the class passed to a routine (such as a print routine) that can therefore handle any report type, because it only uses the methods implemented in the abstract class (whether or not they are overridden in the derived classes).
    • Edited by Acamar Sunday, June 16, 2013 12:09 PM sp
    Sunday, June 16, 2013 11:58 AM
  • Thanks Acamar,

    Looking at the link you kindly posted, there is a sample of MyWashingMachine, it looks to me very difficult to imagine that more than one class has to use the same methods contained in the abstract class WashingMachine...ok ok I know it can! according to your explanation, but it just looks to me that abstract classes are one of those things that you less likely or less common use in your life as a programmer, am I right?


    G.Waters

    Sunday, June 16, 2013 12:16 PM
  • In fact, I would manage my example above like this:

    Public Class Reports
    
        Public Sub Generate(ByVal Query As String)
            'Code to make the report
        End Sub
    
        Public Class InvoicingReports
            Inherits Reports
    
        End Class
    
        Public Class AccountingReports
            Inherits Reports
    
        End Class
    
    End Class
    Much easier to understand and to maintain

    G.Waters

    Sunday, June 16, 2013 12:26 PM
  • Note that a particular advantage of an abstract class is that it can be used as the Type for the class passed to a routine (such as a print routine) that can therefore handle any report type, because it only uses the methods implemented in the abstract class (whether or not they are overridden in the derived classes).


    Would it be too much to ask if you could apply this quote to my report sample?

    G.Waters

    Sunday, June 16, 2013 12:39 PM
  • VB.Net represents a strongly-typed, object-oriented language and as such utilizes "code contracts" to define how objects and their derivatives should behave.

    The fundamental code contract is the Interface.  An Interface defines the core structure of an object, but does not define any actual functionality or member visibility.  Implementers are free to make decisions about if and how each interface member is implemented in their own classes.

    An Abstract Class might be considered a tier-two code contract.  Like an Interface, the Abstract Class will define the core structure of an object, however, it may also define some common functionality and it absolutely defines the member visibility of any derivatives.

    What this means for you as the developer is that you have two subtlety different ways to define a code contract depending on the amount of flexibility you want to offer to derived classes.  Consider the following examples.  Here we will define a code contract for an "Animal" class (the animal kingdom is often a good basis for OOP and Inheritance examples as it follows a relatable pattern and is widely known) and then create two derived classes, Horse and Snake, to demonstrate some of the key differences between using an Interface and using an Abstract Class.

    Namespace InterfaceExample
        Public Interface IAnimal
            ReadOnly Property NumberOfLegs As Integer
            ReadOnly Property SkinCover As SkinCovering
            ReadOnly Property SoundItMakes As String
            ReadOnly Property Kind As AnimalKind
        End Interface
    
        Public Class Horse
            Implements IAnimal
    
            Public ReadOnly Property NumberOfLegs As Integer Implements IAnimal.NumberOfLegs
                Get
                    Return 4
                End Get
            End Property
    
            Public ReadOnly Property SkinCover As SkinCovering Implements IAnimal.SkinCover
                Get
                    Return SkinCovering.Hair
                End Get
            End Property
    
            Public ReadOnly Property SoundItMakes As String Implements IAnimal.SoundItMakes
                Get
                    Return "Neigh"
                End Get
            End Property
    
            Public ReadOnly Property Kind As AnimalKind Implements IAnimal.Kind
                Get
                    Return AnimalKind.Mammal
                End Get
            End Property
        End Class
    
        Public Class Snake
            Implements IAnimal
    
            Protected ReadOnly Property NumberOfLegs As Integer Implements IAnimal.NumberOfLegs
                Get
                    Throw New NotImplementedException()
                End Get
            End Property
    
            Public ReadOnly Property SkinCover As SkinCovering Implements IAnimal.SkinCover
                Get
                    Return SkinCovering.Scales
                End Get
            End Property
    
            Public ReadOnly Property SoundItMakes As String Implements IAnimal.SoundItMakes
                Get
                    Return "Hiss"
                End Get
            End Property
    
            Public ReadOnly Property Kind As AnimalKind Implements IAnimal.Kind
                Get
                    Return AnimalKind.Reptile
                End Get
            End Property
        End Class
    
        Public Enum SkinCovering
            None
            Scales
            Feathers
            Hair
        End Enum
    
        Public Enum AnimalKind
            Other
            Reptile
            Bird
            Mammal
        End Enum
    End Namespace
    
    Namespace AbstractExample
        Public MustInherit Class Animal
            MustOverride ReadOnly Property NumberOfLegs As Integer
            MustOverride ReadOnly Property SkinCover As SkinCovering
            MustOverride ReadOnly Property SoundItMakes As String
    
            Public ReadOnly Property Kind As AnimalKind
                Get
                    Select Case SkinCover
                        Case SkinCovering.Scales
                            Return AnimalKind.Reptile
                        Case SkinCovering.Feathers
                            Return AnimalKind.Bird
                        Case SkinCovering.Hair
                            Return AnimalKind.Mammal
                        Case Else
                            Return AnimalKind.Other
                    End Select
                End Get
            End Property
        End Class
    
        Public Class Horse
            Inherits Animal
    
            Public Overrides ReadOnly Property NumberOfLegs As Integer
                Get
                    Return 4
                End Get
            End Property
    
            Public Overrides ReadOnly Property SkinCover As SkinCovering
                Get
                    Return SkinCovering.Hair
                End Get
            End Property
    
            Public Overrides ReadOnly Property SoundItMakes As String
                Get
                    Return "Neigh"
                End Get
            End Property
        End Class
    
        Public Class Snake
            Inherits Animal
    
            Public Overrides ReadOnly Property NumberOfLegs As Integer
                Get
                    Return 0
                End Get
            End Property
    
            Public Overrides ReadOnly Property SkinCover As SkinCovering
                Get
                    Return SkinCovering.Scales
                End Get
            End Property
    
            Public Overrides ReadOnly Property SoundItMakes As String
                Get
                    Return "Hiss"
                End Get
            End Property
        End Class
    
        Public Enum SkinCovering
            None
            Scales
            Feathers
            Hair
        End Enum
    
        Public Enum AnimalKind
            Other
            Reptile
            Bird
            Mammal
        End Enum
    End Namespace

    As you can see, on the surface, each of these examples does the same thing.  They each provide a code contract for an Animal with the same object members defined. However, there are two major differences in the Horse and Snake classes that derive from Animal between the two examples.

    In the Interface example, we see that the Snake class is free to hide the NumberOfLegs property by changing the member access level.  It can then throw a standard not-implemented exception if that member should be accessed through other means such as reflection.  We also see that both the Horse and Snake class must provide their own implementation of the Kind property to return the respective AnimalKind value.

    In the Abstract Class example the Snake class must implement the NumberOfLegs property and return zero because the parent class has defined it as Public and Abstract Classes cannot change a member's access level (ignoring Shadowing, which breaks strongly typed OOP principle).

    The other difference is that the abstract Animal class itself implements the Kind property for all derived classes because the Abstract Class can provide its own member implementations where an Interface cannot.

    There are other differences between an Interface and an Abstract Class as well, such as the fact that an Abstract Class absolutely defines the lowest-level base class for an object where-as an Interface does not.  For example, a Truck class which inherits from a Machine class could still implement the IAnimal interface and become both a Machine/Truck and an IAnimal, where as a class which inherits from the Animal Abstract Class can never also be a Machine/Truck.

    You gave an example of a "Reports" class in abstract form such that each implementation was able (had to) provide its own logic for generating a report.  You then showed a modification that did away with the abstract base class.  The key difference here is that in the second example, the report generating routine is the same for both derived classes.  If you wanted to later create a third derived class, you would have to edit and recompile the base Reports class to handle the new report generation.  You no longer have a code contract for report generation.  If you stick with the first example using an Abstract Class, then the third derived class would define its own report generation without any modification of the base class (so the new derived class could even be defined in another assembly which references the assembly containing your base class).  In this way you provide a code contract for report generation without limiting the possible number of reports.

    So in summary, an Abstract Class is another type of code contract that a developer can use to provide an initial implementation of core functionality along with a stricter inheritance rule-set for an object which requires additional undefined implementation before it can be used.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Sunday, June 16, 2013 4:27 PM
  • Would it be too much to ask if you could apply this quote to my report sample?


    This example uses the fact that all the reporting classes are derived from the abstract Reports class, so that the instance can be passed as a type Reports to another method (in this case, a prrinting method).

    Depending on what the ReportPrint method needs to do, it might not even convert the instance to the correct type for printing - in that case the addition of a new report type would not require any change to the ReportPrint code.

        Private Sub CreateReport()    
            Select Case ReportType
                Case Invoicing
                    Dim IR As New InvoicingReports
                    'Create an incvoicing report as IR
                    ReportPrint(IR)
    
                Case Accounting
                    Dim AR As New AccountingReports
                    'Create an accounting report as AR
                    ReportPrint(AR)
    
            End Select
    
        End Sub
    
        Public Sub ReportPrint(ByVal Rep As Reports)
            'Print the report
        End Sub
    • Marked as answer by George Waters Sunday, June 16, 2013 10:38 PM
    Sunday, June 16, 2013 10:28 PM
  • Acamar, Reed,

    I really appreciate your help, you just opened my understanding about abstract class and interfaces, if I could I would invite you some beers guys.

    Thank you both very much for your time, I'm pretty sure this will help others with the same doubt.


    G.Waters

    Sunday, June 16, 2013 10:44 PM