|
-
Sep 21st, 2010, 10:52 AM
#1
Thread Starter
Addicted Member
Object design
Ok, this question is a bit hard to formulate, when not knowing the answers.. So plz bear with me..
I think (?) this is about object design, and perhaps about structural and/or creational patterns. But a bit hard to say when I only know a couple of patterns yet..
Consider these classes:
Code:
Class A
public products as List(Of Product)
Class Product
public nameID as String
public component as Component
Class Component
public nameID as String
public t As T
So; In class A, each element of the products list has their own component instance, which can all change independently and being saved in this nested structure if an instance of A is serialized to file.
But... what if we INSTEAD want each Product instance only to (in some way) refer to one instance of a specific list of Components. So we wan't to save the products list to one file, and the components list to another file. Then would I build the objects the same way, or different? So let's then say we have now Class B as below.
Code:
Class B
public products as List(Of Product)
public components as List(Of Component)
Now, how would the Product class look like in this case? Exactly the same as above? My intuition says it would now be smarter to just refer to the Component with an ID property:
Code:
Class Product
public nameID as String
public componentID as String
Why?
1. Will not need to update each component variable of the products list when the components list changes.
2. If the Product object shall be shown in a DataGridView, for editing properties, it seems easier to implement the selection of the component property.
Is this wrong approach? Is it suitable in some cases, and if so, when? Are there other solutions I perhaps am not aware of that would be appropriate?
Is this about structural design patterns? and if so, can the alternatives above be catagorized to some pattern type?
I don't really know exactly what I'm looking for.. But I think probably I'm looking to understand whether the object design will be determined by the way the objects are saved.. And if i shall save the objects by serializing to file, vs saving to a database, would I create the business objects differently?
-
Sep 21st, 2010, 12:11 PM
#2
Re: Object design
I think your original design is just fine. You have some number of components. Each product refers to a list of some subset of these components. This is pretty standard.
If you wanted to save this, you might be in a pickle. There's things to consider. Do you want reference equality, or is value equality sufficient. In other words, if you have component x and products a and b both need to refer to it, would you care if they refer to two different objects with the same property values or would you prefer one object?
If you don't care if they share the same object, implementation of saving the information is straightforward. To save a component, write out its properties. To save a product, write its properties followed by a list of its components. To save whatever A is, write a list of products. Easy. Loading goes in reverse: create an A, read its properties, for each product in its list read the product; to read a product you read each of its properties and read each component in its list. It's easier to code than explain, actually.
If you want to share object instances, you are right to deduce you need an ID. Each component would need an ID property, and each product would need an ID property. After that, you just need to slightly change the process of saving the information. To write a component, write its properties out. To write a list of components, just write each component out. To write a product, write each of its properties *but* instead of writing its list of components, just write a list of component IDs. Likewise to write A's list of products, you need only write a list of product IDs. This works best if you write to a file in a specific order: first write the list of components out as they have no dependencies. Then write a list of all products, since they need to use the IDs from the component list. Then write a list of all A instances, since they need the other two lists. Loading works backwards. Load the list of components. Now load the list of products; to convert the list of component IDs into components, refer to the list you created in the last step. To load the A instances, refer to the list of all products. That one is *much* easier to code than explain.
Of course, this data structure is screaming for a database for data storage. This would encourage the "sharing objects" approach, and it might be a little easier to manage. That's a whole different set of skills though.
I'll try and address your questions at the end. Generally, you don't want anything in your business objects to be influenced by your data store. The whole point of separate layers is so you can change lower layers without affecting higher layers. So if you designed your business object in such a way that it made writing to files easier, you break the layering. What happens if you decide to switch to a database and the business objects are no longer convenient? If you change them, you have to change everything that touches the business objects. That's not good! It is better to design the business objects so they are easy for the business logic to use; purists will point out you should even ignore UI concerns at this layer. When you design your data access layer, it should have facilities for converting raw data into business objects. That way, you can change your data access without making the change apparent to the higher layers. It may mean the data access layer has to use some intermediate objects or data structures for convenience; that's fine.
-
Sep 21st, 2010, 01:52 PM
#3
Re: Object design
I would add that serializing to a file probably would influence your design, but should only be used if the list is not going to grow much once created. Binary serialization can be done without worrying about how the classes are designed, though, so either approach would work fine. Whether there is some advantage to one over the other as far as serialization is concerned, I really doubt. However, if you expect the number of components or products to increase steadily over time, then it certainly sounds like a DB would be more appropriate than any kind of file serialization.
Also, the ID for the component shouldn't be a string. It should be an integer unless you have sound reason to use something like a GUID. Integers will be much faster for looking up than strings would be.
My usual boring signature: Nothing
 
-
Sep 21st, 2010, 03:11 PM
#4
Re: Object design
 Originally Posted by Shaggy Hiker
I would add that serializing to a file probably would influence your design, [...]
It need not be that way:
Code:
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports System.Linq
Module Module1
Sub Main()
Dim employees As New List(Of Business.Employee)
Dim departments As New List(Of Business.Department)
Dim company As New Business.Company()
departments.Add(New Business.Department() With {.Id = 0, .Name = "zero"})
departments.Add(New Business.Department() With {.Id = 1, .Name = "one"})
employees.Add(New Business.Employee() With {.Department = departments(0), .Name = "Bob"})
employees.Add(New Business.Employee() With {.Department = departments(1), .Name = "Alice"})
company.Employees = employees.ToArray()
Console.WriteLine("Before serialization:")
PrintCompany(company)
Dim dataAccess As New DataAccess.DataStore()
dataAccess.SaveData(company.Employees)
company = Nothing
departments.Clear()
employees.Clear()
company = dataAccess.LoadData()
Console.WriteLine("After serialization:")
PrintCompany(company)
End Sub
Private Sub PrintCompany(ByVal company As Business.Company)
Console.WriteLine("Company has {0} employees.", company.Employees.Length)
For Each employee As Business.Employee In company.Employees
Console.WriteLine(" {0} is in department {1}.", employee.Name, employee.Department.Name)
Next
End Sub
End Module
Namespace DataAccess
<Serializable()>
Class SerializableEmployee
Public Sub New(ByVal employee As Business.Employee)
Department = New SerializableDepartment(employee.Department)
Name = employee.Name
End Sub
Public Property Department As SerializableDepartment
Public Property Name As String
End Class
<Serializable()> _
Class SerializableDepartment
Public Sub New(ByVal department As Business.Department)
Id = department.Id
Name = department.Name
End Sub
Public Property Id As Integer
Public Property Name As String
End Class
Class DataStore
Public Sub SaveData(ByVal employees As IEnumerable(Of Business.Employee))
Dim mappedEmployees = (From e As Business.Employee In employees
Select New SerializableEmployee(e)).ToArray()
Using output As New FileStream("data.dat", FileMode.Create)
Dim formatter As New BinaryFormatter()
formatter.Serialize(output, mappedEmployees)
End Using
End Sub
Public Function LoadData() As Business.Company
Dim mappedEmployees As IEnumerable(Of SerializableEmployee)
Using input As New FileStream("data.dat", FileMode.Open)
Dim formatter As New BinaryFormatter()
mappedEmployees = CType(formatter.Deserialize(input), IEnumerable(Of SerializableEmployee))
End Using
Dim departments As IList(Of Business.Department) = (From e As SerializableEmployee In mappedEmployees
Select sd = e.Department Distinct
Select New Business.Department With
{
.Id = sd.Id,
.Name = sd.Name
}).ToList()
' I tried some fancy tricks to make the query automatically set the department, but
' it was a pain in the butt. Inline lambda syntax felt more natural. This isn't important
' to the example anyway.
Dim employees As IEnumerable(Of Business.Employee) =
mappedEmployees.Select(Function(e As SerializableEmployee)
Dim newEmployee As New Business.Employee
newEmployee.Department = (From d In departments
Where d.Id = e.Department.Id
Select d).First()
newEmployee.Name = e.Name
Return newEmployee
End Function)
Dim company As New Business.Company With {.Employees = employees.ToArray()}
Return company
End Function
End Class
End Namespace
Namespace Business
Class Company
Public Employees As Employee()
End Class
' Note the lack of serialization attributes
Class Employee
Public Department As Department
Public Name As String
End Class
Class Department
Public Property Id As Integer
Public Property Name As String
End Class
End Namespace
The LINQ is there just to get the grunt work done in minimal space, it's not part of the example. There's some design flaws in it as well but it demonstrates the point I want to make.
If the data access layer knows there is a difference between the business objects and the objects that are more convenient for it, it can perform a mapping to and from the types it can work with. Thus, the data layer in this application defines its own version of the business objects that are more convenient (since they're serializable). It's a bit contrived because there's nothing in the design of the business objects that would make them impossible to mark as serializable, but it's not hard to imagine constraints that could cause this state. You could certainly have requirements that make it difficult to map to serializable types, but I'd take that as an indicator that binary serialization just isn't a good fit for the project.
Implementations of the MVVM pattern in WPF tend to take this a little further and define ViewModel classes that make the business layer classes more friendly to WPF's data binding features. Thus, in these applications, you end up with a business object and at least one "wrapper" type that makes the business object look like something else. I think this is a use of the Adapter pattern, but can't decide if it fits perfectly.
This notion of having different versions of the types in each layer is a concept commonenough that people wrote frameworks to help with the conversions. Object Relational Mapping (ORM) software takes some of the burden away and in some cases can automatically generate the wrapper classes. Common .NET ORM libraries are the ADO .NET EF, Castle ActiveRecord, and NHibernate.
-
Sep 21st, 2010, 03:43 PM
#5
Thread Starter
Addicted Member
Re: Object design
Thanks guys!
Ok, I was confused with my example, but I believe maybe I see your points.
So if a business object is to be serialized binary, I guess I should just avoid those ID properties all together and instead have a variable of the child Class instance..
And an object I know needs to be saved to a database (in later development), for convenience, or if at no disadvantage, I could keep it as a non-nested object, and rather introduce an ID field? But if sacrificing any logic, more appropriate would be not to, and instead deal with that in ORM logic?
..I guess ID fields in general has perhaps nothing to do in the BLL at all (?)
Last edited by bretddog; Sep 21st, 2010 at 03:48 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|