I have a case as follows (VB.NET):
class A (contains some fields)
class B inherits List(Of A)
class C (contains some fields)
class D inherits List(Of C)
There are some common Methods and Functions that I want them to be centralized and act on both classes B & D, without the need to re-write them and keep them updated under both classes.
Those common Methods and Functions act on identical fields (name and type) in both classes A and C.
What should I do? And why to inherit from List(Of T) is considered a bad idea?
My solution was to create new Abstract (MustInherit) class X and moves the common fields from Classes A & C to class X. Then created a 'Friend' Module that has those common methods and functions (targeting the common fields between classes A & C) which takes IEnumerable(Of class X) .. and voila!
Any comments on such implementation??
Thanks
The below is my solution using IEnumerable, but using a shared function in a separate module.
Public MustInherit Class ProbSts 'class X
Public Status As Boolean
Public Probability, Min, Max As Integer
End Class
Public NotInheritable Class SIPRouteProbabilityStatus 'class A
Inherits ProbSts
Public Route As SIPRoute
End Class
Public NotInheritable Class SIPRGProbabilityStatus ' class C
Inherits ProbSts
Public RG As SIPRouteGroup
End Class
Public NotInheritable Class SIPRouteProbabilityStatusCollection ' class B
Inherits List(Of SIPRouteProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Public NotInheritable Class SIPRGProbabilityStatusCollection 'class D
Inherits List(Of SIPRGProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Friend Module ProbabilityAction
Public Function TotalProbabilityAction(ProbStsList As IEnumerable(Of ProbSts), Optional Status As Boolean? = Nothing) As Integer
Dim sum As Integer = 0
For Each x In ProbStsList
If Status.HasValue Then
If x.Status = Status.Value Then sum += x.Probability
Else
sum += x.Probability
End If
Next
Return sum
End Function
End Module
There is nothing wrong with inheriting from List(Of T) when done properly. It's hard to give the best solution without seeing your classes and how you're using them, but your solution of inheriting from an abstract class is one way of solving it. However from your description, implementing an interface seems to be a better fit. Convert X to an interface with the common fields and you'll be good to go. The actual fields will stay in class A and C, yet the external functions will know how to access and use them.
What's the difference between using an interface vs. an abstract class in your case?
Interface: it only defines the common fields and methods of the classes and leaves the implementation to them.
Abstract Class: It actually hosts the common fields and methods with the implementation so the classes don't need to duplicate the code in case it is the same.
EDIT I don't know if this is the best solution, because I need to see how all these classes are used in your code, but you can get rid of the module by converting it to a class that inherits from List(Of T) and then inherit this class instead of List(Of T) in your B and D classes. Something like this:
Public MustInherit Class ProbSts 'class X
Public Status As Boolean
Public Probability, Min, Max As Integer
End Class
Public NotInheritable Class SIPRouteProbabilityStatus 'class A
Inherits ProbSts
Public Route As SIPRoute
End Class
Public NotInheritable Class SIPRGProbabilityStatus ' class C
Inherits ProbSts
Public RG As SIPRouteGroup
End Class
Public MustInherit Class ProbabilityAction(Of T)
Inherits List(Of T)
Public Function TotalProbabilityAction(ProbStsList As IEnumerable(Of ProbSts), Optional Status As Boolean? = Nothing) As Integer
Dim sum As Integer = 0
For Each x In ProbStsList
If Status.HasValue Then
If x.Status = Status.Value Then sum += x.Probability
Else
sum += x.Probability
End If
Next
Return sum
End Function
End Class
Public NotInheritable Class SIPRouteProbabilityStatusCollection ' class B
Inherits ProbabilityAction(Of SIPRouteProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
Public NotInheritable Class SIPRGProbabilityStatusCollection 'class D
Inherits ProbabilityAction(Of SIPRGProbabilityStatus)
Public ProbabilityTotal As Integer
Public Function TotalProbability(Optional Status As Boolean? = Nothing) As Integer
Return TotalProbabilityAction(Me, Status)
End Function
End Class
I kept the original name of the module ProbabilityAction, but it's recommended to change it to a more suitable name for a base class. I also followed your way and qualified it with MustInherit, but you can decide if you want that or not.
Related
Is there a way to spread interface implementation over class hierarchy? Consider following example.
Public Interface I
Property X As String
Property Y As String
Property Z As String
End Interface
Public Class A
Property X As String
Property Y As String
End Class
Public Class B
Inherits A
Implements I
Property Z As String
End Class
I'd like to avoid to repeat X and Y in B. Think about ten instead of two.
And I don't want to split interface I because inheritance of B should be implementation detail.
This might not work for your situation but you could use Partial Classes.
This means you will lose class A :-(
Are you using MVC Razor HTML helper classes? I don't think they like it when as interface isn't implemented in a single class.
Public Interface I
Property X As String
Property Y As String
Property Z As String
End Interface
Partial Public Class B
Property X As String Implements I.X
Property Y As String Implements I.Y
End Class
Public Class B
Implements I
Property Z As String Implements I.Z
End Class
Not sure how else to tackle this one...
EDIT:
Public Interface iA
Property X As String
Property Y As String
End Interface
Public Interface iB
Property Z As String
End Interface
Public Interface IAB
Inherits iA
Inherits iB
End Interface
Public Class A
Implements iA
Property X As String Implements iA.X
Property Y As String Implements iA.Y
End Class
Public Class B
Inherits A
Implements IAB
Property Z As String Implements iB.Z
End Class
I have an abstract "GridBase" class with two types of derived classes "DetailGrid" and "HeaderGrid".
Respectively, one is comprised of "DetailRow" objects and the other "HeaderRow" objects. Both of those inherit from a "RowBase" abstract class.
What I am trying to do is the following:
Public MustInherit Class GridBase
Private pRows As List(Of RowBase)
Public ReadOnly Property Rows As List(Of RowBase)
Get
Return pRows
End Get
End Property
End Class
Public Class DetailGrid
Inherits GridBase
End Class
In this scenario, I want DetailGrid.Rows to return a list of DetailRow. I want HeaderRow.Rows to return a list of HeaderRow. Am I on the right track with this or should the Rows property not be included in the GridBase class?
If you want a stronger typing guarantee, then you probably want:
Public MustInherit Class GridBase(Of T as RowBase)
Private pRows As List(Of T)
Public ReadOnly Property Rows As List(Of T)
Get
Return pRows
End Get
End Property
End Class
Public Class DetailGrid
Inherits GridBase(Of DetailRow)
End Class
If I have a class called A and a class called B, if B inherits A that means A is the super class and B is the subclass. I have been asked to describe why class A is not an abstract class but as i see it class A is an abstract class A, as it has been created for Class B to use in the future, is it something to do with Class B not being able to access the fields in Class A as although they are private by default?
Class A looks something like this
Public Class A
StartDate As Date
Men As Integer
Place As String
Public Sub New()
StartDate = Today
Men = 0
Place = ""
End Sub
End Class
Class B Looks like this
Public Class B inherits Class A
Grade As ExamGrade
Public Sub New()
MyBase.New
StartDate = Today
Men = 0
Place = ""
Grade = 'Easy'
End Sub
Public Function setGrade(grade As String)
ExamGrade = grade
End Function
End Class
In order to be abstract, class A must have the MustInherit keyword.
Abstract (MustInherit) means that this class serves as base class only and cannot be instantiated with New. It also allows you to declare abstract (MustInherit) members with no implementation, i.e. no method body. The inheriting classes then must override the abstract members and provide an implementation unless they are abstract themselves (where a third level of deriving classes would then provide an implementation).
Note that you are allowed to call an abstract member. At runtime the implementation of the actual implementing class will be called.
See: MustInherit (Visual Basic)
Members are private if not specified otherwise. Specify them to be Protected to allow descendant classes to see them or Public to allow "everybody" to see them.
See: Access Levels in Visual Basic
Public MustInherit ClassA
Protected StartDate As Date
Protected Men As Integer
Protected Place As String
Public Sub New()
StartDate = Today
Men = 0
Place = ""
End Sub
Public MustOverride Sub Print()
End Class
Public ClassB
Inherits ClassA
Public Grade As String
Public Sub New()
MyBase.New() 'This initializes StartDate, Men and Place
Grade = "Easy"
End Sub
Public Sub SetGrade(ByVal grade As String)
Me.Grade = grade
End Sub
Public Overrides Sub Print()
Console.WriteLine($"Grade = {Grade}")
End Sub
End Class
Now, you can use it like this
Sub Test(ByVal a As ClassA)
a.Print()
End Sub
You can call Test by passing it a ClassB object.
A is not abstract. Abstract means you cannot instantiate the class. It means you MUST inherit it.
Use the abstract keyword to make the class abstract. You can also make methods abstract as well.
If you want B to see certain methods in A, but not to anyone else, use protected keyword.
Sorry, VB uses the MustInherit and MustOverride keywords.
At the moment I'm trying to create a kind of model in vb.net which can be used to create/fetch database entrys.
I created a main class Model with a shared function to fetch the datasets, e.g. Model.find().
Now I'd like to create Classes which inherit the main Model-Class, e.g. a separate one for users: UserModel.find() => "SELECT * FROM users".
What I need now is to find a way to tell the Class which table it should use. I thought about an abstract String "table" which is a constant in each "child-model", but how could this be implemented as it's not possible to override shared members?
Thanks in advance!
Edit: Maybe this will make it a little clearer what I mean:
Public Class Model
Public Shared _controller As Controller
Public Shared table As String
Protected Shared tableFields As String()
Shared reader As Npgsql.NpgsqlDataReader
Public Shared Function find()
Dim a As ArrayList = New ArrayList
'Test if the tablefields are already known to the class, if not, get them
If tableFields Is Nothing Then
getTableFields()
End If
Dim query As String = "SELECT " + String.Join(", ", tableFields) + " FROM " + table
reader = _controller.executeReader(query)
While reader.Read
o = New Model
Dim v As New Hashtable
For Each field In tableFields
v(field) = reader(field)
Next
o.values = v
a.Add(o)
End While
reader.Close()
Return DirectCast(a.ToArray(GetType(Model)), Model())
End Function
Public values As Hashtable
Public Sub New()
End Sub
End Class
So I want a shared method which finds all database entries and gives back an array of instances of its own type, e.g. Model().
That's why I wanted to keep the find-method shared and not bound to an instance.
I think you could use Generics. Here I´ve pasted an example
All the classes in your domain could inherit from Entity class
Public MustInherit Class Entity
'...
End Class
Your Model class, with your method Find
Public Class Model
Public Shared Sub Find(Of T As Entity)()
' You could know the name of T to find the table
Dim tableName As String = GetType(T).Name
'...
End Sub
End Class
One class of your domain, for example: User class
Public Class User
Inherits Entity
' ...
End Class
And finally, an example of how could you instantiate the Find method
Model.Find(Of User)()
'...
I dunno if this is what you mean, do you find this helpfull?
You could make your main class abstract and each subclass will have to return its "own" table name via its own implementation (e.g. getTableName). This way, you would only have to maintain you method logic in the main class.
It is common to use the Singleton design pattern in such cases: create an instance method, overridden by inheriting classes. Each inheriting class should have that instance method return a Singleton object related to that class.
Here is one way of doing it:
MustInherit Class BaseClass
Public MustOverride Function getTableName() As String
End Class
Class Class1
Inherits BaseClass
Private Shared TableName As String = "myTable1"
Public Overrides Function getTableName() As String
Return TableName
End Function
End Class
Class Class2
Inherits BaseClass
Private Shared TableName As String = "myTable2"
Public Overrides Function getTableName() As String
Return TableName
End Function
End Class
EDIT: a whole different approach.
You can have the base class hold some dictionary, which relates class types (or type names) with the correct table:
Class BaseClass
Private Shared myDictionary As New Collections.Generic.Dictionary(Of Type, String)
Friend Shared Sub RegisterType(ByVal childType As Type, ByVal tableName As String)
myDictionary.Add(childType, tableName)
End Sub
Public Shared Function getTableName(ByVal childType As Type) As String
Return myDictionary.Item(childType)
End Function
End Class
Class Class1
Shared Sub New()
BaseClass.RegisterType(GetType(Class1), "table1")
End Sub
End Class
Class Class2
Shared Sub New()
BaseClass.RegisterType(GetType(Class2), "table2")
End Sub
End Class
Shared (static) objects or object members can't be inherited or overrided. Inheritence is for instance of an object. Since you do not have to instantiate a static class you can't inherit from it. The same with the methods. A static method shouldn't be virtual (Overridable in VB) as it defines a method that perform tasks with no instance of the class. Then this makes it impossible to use instance fields or properties within a static (Shared in VB) method. This is a bad design trying so.
In fact, every static (Shared) classes should be marked as NotInheritable in VB, and defining the default empty constructor only. This is a leak from VB in regards of OOP concepts.
I have the following sample code in a VB.NET console application. It compiles and works, but feels like a hack. Is there a way to define EmptyChild so that it inherits from Intermediate(Of T As Class) without using the dummy EmptyClass?
Module Module1
Sub Main()
Dim Child1 = New RealChild()
Child1.Content = New RealClass()
Dim Child2 = New EmptyChild()
Console.WriteLine("RealChild says: " & Child1.Test)
Console.WriteLine("EmptyChild says: " & Child2.Test)
Console.ReadLine()
End Sub
Public Class EmptyClass
End Class
Public Class RealClass
Public Overrides Function ToString() As String
Return "This is the RealClass"
End Function
End Class
Public MustInherit Class Base(Of T As Class)
Private _content As T = Nothing
Public Property Content() As T
Get
Return _content
End Get
Set(ByVal value As T)
_content = value
End Set
End Property
Public Overridable Function Test() As String
If Me._content IsNot Nothing Then
Return Me._content.ToString
Else
Return "Content not initialized."
End If
End Function
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Base(Of T)
'some methods/properties here needed by Child classes
End Class
Public Class RealChild
Inherits Intermediate(Of RealClass)
'This class needs all functionality from Intermediate.
End Class
Public Class EmptyChild
Inherits Intermediate(Of EmptyClass)
'This class needs some functionality from Intermediate,
' but not the Content as T property.
Public Overrides Function Test() As String
Return "We don't care about Content property or Type T here."
End Function
End Class
End Module
The other way to do this would be to move the generic code out of the Base class and then create 2 Intermediate classes like this:
Public MustInherit Class Intermediate
Inherits Base
'some methods/properties here needed by Child classes
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Intermediate
'implement generic Content property here
End Class
Then RealChild would inherit from the generic Intermediate and EmptyChild would inherit from the non-generic Intermediate. My problem with that solution is that the Base class is in a separate assembly and I need to keep the code that handles the generic type in that assembly. And there is functionality in the Intermediate class that does not belong in the assembly with the Base class.
Yes, you need to specify a type parameter when you inherit, or your EmptyChild must be generic as well. But, you don't have to dummy up a EmptyClass - just use Object as your type parameter:
Public Class EmptyClass
Inherits Intermediate(Of Object)
End Class