Results 1 to 5 of 5

Thread: Object design

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Jan 2009
    Location
    Norway
    Posts
    185

    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?


  2. #2
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    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.

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    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

  4. #4
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Object design

    Quote Originally Posted by Shaggy Hiker View Post
    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.

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Jan 2009
    Location
    Norway
    Posts
    185

    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
  •  



Click Here to Expand Forum to Full Width