Solved

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

Posted on 2014-11-12
6
102 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
MS Dynamics Made Instantly Simpler

Make Your Microsoft Dynamics Investment Count  & Drastically Decrease Training Time by Providing Intuitive Step-By-Step WalkThru Tutorials.

 

Author Closing Comment

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

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 34

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

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This tutorial demonstrates one way to create an application that runs without any Forms but still has a GUI presence via an Icon in the System Tray. The magic lies in Inheriting from the ApplicationContext Class and passing that to Application.Ru…
If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.

691 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