Solved

How can I change this code example to achieve a more flexible alternative?

Posted on 2014-11-12
6
88 Views
Last Modified: 2014-11-12
I am using a cache class which stores sets of data held within classes for different periods.  I've been trying to enhance this to include sets of data from other areas.  I would like to reuse much of the code which operates and keeps the Cache up to date.
In the example below you will see that the AddData and Add methods implement the SalesData type of class.
How would I make the AddData and Add methods work irrespective of the Class type, provided both use a start and end date constructor.
I realise I can repeat each method in each inheriting child class but in my implementation these methods are quite a bit longer and I am seeking where possible to keep the code reusable and manageable.
I am thinking that polymorphism and interfaces may play a part but I don't have the understanding to apply them.

Public Class GenericCache
    Inherits System.Collections.CollectionBase
    Dim pStartDate, pEndDate As DateTime
    Private Sub Add(ByVal cSalesData As SalesData)
        Me.List.Add(cSalesData)
    End Sub
    Sub AddData()
        Me.Add(New SalesData("2001-01-01", "2001-01-02"))
        Me.Add(New SalesData("2001-01-02", "2001-01-03"))
        Me.Add(New SalesData("2001-01-04", "2001-01-05"))
    End Sub    
End Class
Public Class CachedSalesData
    Inherits GenericCache
    'properties and methods particular to SalesCache
End Class

Public Class CachedOrdersData
    Inherits GenericCache
    'properties and methods particular to OrdersCache
End Class

Public Class SalesData
    Public Sub New(StartDate As DateTime, EndDate As DateTime)
        'Looks up Sales for dates supplied
    End Sub
End Class

Public Class OrdersData
    Public Sub New(StartDate As DateTime, EndDate As DateTime)
        'Looks up Orders for dates supplied
    End Sub
End Class

Open in new window

0
Comment
Question by:dgloveruk
  • 2
  • 2
  • 2
6 Comments
 
LVL 17

Expert Comment

by:OriNetworks
ID: 40438000
Instead of stricly delcaring the incoming object as SalesData you can delcare it as a generic Object
 Private Sub Add(ByVal cSalesData As Object)

Depending on how you select the data to add and retireve you can check the object type and/or Cast the object to the correct type. e.g. obj.GetType()
0
 

Author Comment

by:dgloveruk
ID: 40438028
Something I wanted to do was that in the CachedSalesData class I would run adddata and it would effectively do this :
 Me.Add(New SalesData("2001-01-01", "2001-01-02"))
but if I ran adddata on a CachedOrdersData class it would use the same adddata sub but do this :
 Me.Add(New OrderData("2001-01-01", "2001-01-02"))

Note that I can't syntactically use  Me.Add(New Object("2001-01-01", "2001-01-02"))
0
 
LVL 17

Accepted Solution

by:
OriNetworks earned 500 total points
ID: 40438046
Correct. You would still use Me.Add(New SalesData(.... or New OrderData

Private Sub Add(ByVal cSalesData As SalesData)
        Me.List.Add(cSalesData)
    End Sub

Open in new window

would change to
Private Sub Add(ByVal cGenericData As Object)
        Me.List.Add(cGenericData)
    End Sub

Open in new window


I like to keep things separate so I personally would keep the objects in separate lists but this would be the way to do it with one generic list of dynamic objects.
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Closing Comment

by:dgloveruk
ID: 40438062
Ok thank you!
0
 
LVL 32

Expert Comment

by:it_saige
ID: 40438128
I never recommend using a loose Object declaration unless you absolutely have to.  It breaks type safety.

If I am understanding your requirements, you could do something like this:
Class PolymorphismExample
	Public Class GenericCache
		Inherits System.Collections.CollectionBase
		Private Sub Add(ByVal cCacheData As ICacheData)
			Me.List.Add(cCacheData)
		End Sub

		Sub AddData()
			Me.Add(New SalesData("2001-01-01", "2001-01-02"))
			Me.Add(New OrderData("2001-01-01", "2001-01-02"))
			Me.Add(New SalesData("2001-01-02", "2001-01-03"))
			Me.Add(New OrderData("2001-01-02", "2001-01-03"))
			Me.Add(New SalesData("2001-01-04", "2001-01-05"))
			Me.Add(New OrderData("2001-01-03", "2001-01-04"))
		End Sub
	End Class

	Public Interface ICacheData
		Property StartDate() As DateTime
		Property EndDate() As DateTime
	End Interface

	Public Class SalesData
		Implements ICacheData
		Private pStartDate As DateTime
		Private pEndDate As DateTime

		Public Property EndDate As Date Implements ICacheData.EndDate
			Get
				Return pEndDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pEndDate = value
				End If
			End Set
		End Property

		Public Property StartDate As Date Implements ICacheData.StartDate
			Get
				Return pStartDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pStartDate = value
				End If
			End Set
		End Property

		Public Sub New(ByVal StartDate As String, ByVal EndDate As String)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As String)
			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If

			Me.StartDate = StartDate
		End Sub

		Public Sub New(ByVal StartDate As String, ByVal EndDate As DateTime)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			Me.EndDate = EndDate
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As DateTime)
			Me.StartDate = StartDate
			Me.EndDate = EndDate
		End Sub
	End Class

	Public Class OrderData
		Implements ICacheData
		Private pStartDate As DateTime
		Private pEndDate As DateTime

		Public Property EndDate As Date Implements ICacheData.EndDate
			Get
				Return pEndDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pEndDate = value
				End If
			End Set
		End Property

		Public Property StartDate As Date Implements ICacheData.StartDate
			Get
				Return pStartDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pStartDate = value
				End If
			End Set
		End Property

		Public Sub New(ByVal StartDate As String, ByVal EndDate As String)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As String)
			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If

			Me.StartDate = StartDate
		End Sub

		Public Sub New(ByVal StartDate As String, ByVal EndDate As DateTime)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			Me.EndDate = EndDate
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As DateTime)
			Me.StartDate = StartDate
			Me.EndDate = EndDate
		End Sub
	End Class

	Shared Sub Main()
		Try
			Dim cache As New GenericCache()
			cache.AddData()
			For Each item As ICacheData In cache
				Console.WriteLine(String.Format("Cache Item Type: {0}; Start Date: {1:MM/dd/yyyy}; End Date: {2:MM/dd/yyyy}", item.GetType(), item.StartDate, item.EndDate))
			Next
		Catch e As Exception
			Console.WriteLine(e.ToString())
		End Try
		Console.ReadLine()
	End Sub
End Class

Open in new window


Produces the following output:Capture.JPG
0
 
LVL 32

Expert Comment

by:it_saige
ID: 40438363
A word on why it breaks type safety.  Imagine 6 months from now, you (or someone else) add a new class FooData but do not implement FooData with a StartDate and/or EndDate, FooData can still be added to the cache because FooData *is* an Object but now you have to check to see if FooData has a StartDate and EndDate (per your requirements) or a call to the returned Object to pull the StartDate and/or EndDate will end in an exception.

Example:
Class PolymorphismExample
	Public Class GenericCache
		Inherits System.Collections.CollectionBase
		Private Sub Add(ByVal cCacheData As ICacheData)
			Me.List.Add(cCacheData)
		End Sub

		Private Sub Add(ByVal cCachData As Object)
			Me.List.Add(cCachData)
		End Sub

		Sub AddData()
			Me.Add(New SalesData("2001-01-01", "2001-01-02"))
			Me.Add(New OrderData("2001-01-01", "2001-01-02"))
			Me.Add(New SalesData("2001-01-02", "2001-01-03"))
			Me.Add(New OrderData("2001-01-02", "2001-01-03"))
			Me.Add(New SalesData("2001-01-04", "2001-01-05"))
			Me.Add(New OrderData("2001-01-03", "2001-01-04"))
			Me.Add(New FooData())
		End Sub
	End Class

	Public Interface ICacheData
		Property StartDate() As DateTime
		Property EndDate() As DateTime
	End Interface

	Public Class SalesData
		Implements ICacheData
		Private pStartDate As DateTime
		Private pEndDate As DateTime

		Public Property EndDate As Date Implements ICacheData.EndDate
			Get
				Return pEndDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pEndDate = value
				End If
			End Set
		End Property

		Public Property StartDate As Date Implements ICacheData.StartDate
			Get
				Return pStartDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pStartDate = value
				End If
			End Set
		End Property

		Public Sub New(ByVal StartDate As String, ByVal EndDate As String)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As String)
			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If

			Me.StartDate = StartDate
		End Sub

		Public Sub New(ByVal StartDate As String, ByVal EndDate As DateTime)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			Me.EndDate = EndDate
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As DateTime)
			Me.StartDate = StartDate
			Me.EndDate = EndDate
		End Sub
	End Class

	Public Class OrderData
		Implements ICacheData
		Private pStartDate As DateTime
		Private pEndDate As DateTime

		Public Property EndDate As Date Implements ICacheData.EndDate
			Get
				Return pEndDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pEndDate = value
				End If
			End Set
		End Property

		Public Property StartDate As Date Implements ICacheData.StartDate
			Get
				Return pStartDate
			End Get
			Set(ByVal value As Date)
				If Not value.Equals(pEndDate) Then
					pStartDate = value
				End If
			End Set
		End Property

		Public Sub New(ByVal StartDate As String, ByVal EndDate As String)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As String)
			If Not DateTime.TryParse(EndDate, Me.EndDate) Then
				Throw New ArgumentException("EndDate must be a valid DateTime value", "EndDate")
			End If

			Me.StartDate = StartDate
		End Sub

		Public Sub New(ByVal StartDate As String, ByVal EndDate As DateTime)
			If Not DateTime.TryParse(StartDate, Me.StartDate) Then
				Throw New ArgumentException("StartDate must be a valid DateTime value", "StartDate")
			End If

			Me.EndDate = EndDate
		End Sub

		Public Sub New(ByVal StartDate As DateTime, ByVal EndDate As DateTime)
			Me.StartDate = StartDate
			Me.EndDate = EndDate
		End Sub
	End Class

	Public Class FooData
		Public Overrides Function ToString() As String
			Return "I am an evil foo data object; prepare for an exception"
		End Function
	End Class

	Shared Sub Main()
		Try
			Dim cache As New GenericCache()
			cache.AddData()
			For Each item In cache
				Console.WriteLine(String.Format("Cache Item Type: {0}; Start Date: {1:MM/dd/yyyy}; End Date: {2:MM/dd/yyyy}", item.GetType(), If(TypeOf item Is ICacheData, TryCast(item, ICacheData).StartDate, item), If(TypeOf item Is ICacheData, TryCast(item, ICacheData).EndDate, item)))
			Next
		Catch e As Exception
			Console.WriteLine(e.ToString())
		End Try
		Console.ReadLine()
	End Sub

Open in new window


Produces the following output:Capture.JPG
-saige-
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

I think the Typed DataTable and Typed DataSet are very good options when working with data, but I don't like auto-generated code. First, I create an Abstract Class for my DataTables Common Code.  This class Inherits from DataTable. Also, it can …
Since .Net 2.0, Visual Basic has made it easy to create a splash screen and set it via the "Splash Screen" drop down in the Project Properties.  A splash screen set in this manner is automatically created, displayed and closed by the framework itsel…
This video discusses moving either the default database or any database to a new volume.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now