OOP object collections

R

RSH

Hi,

I have a basic question around how to handle collections of objects when
using object composition.

In my sample below I have a typical arrangement, I have a Company class and
a Employee class. My question is whether I should create a collection IN the
company class to store the employees, or as in my sample below, create a
EmployeeCollection class which is responsible for managing the collection of
employees?

It seems like it is a better construct to have a seperate class to manage
the collection...am I wrong there?

Thanks,
Ron

public class Company
{
private int m_Id;
private string m_Name;
private EmployeeCollection m_EmployeeCollection = null;

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }

protected void LoadEmployees()
{
Employee employee = null;
m_EmployeeCollection = new EmployeeCollection(this);
// Load employees from datasource into EmployeeCollection object
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
}
}

public class EmployeeCollection
{
private List<Employee> m_EmployeeList = new List<Employee>();
private Company m_Parent = null;

public EmployeeCollection(Company Parent)
{
m_Parent = Parent;
}

public IEnumerable<Employee> GetEmployees()
{
foreach (Employee employee in m_EmployeeList)
{
yield return employee;
}
}

public void Add(Employee employee)
{
m_EmployeeList.Add(employee);
}

public void Remove(int Id)
{
foreach (Employee employee in m_EmployeeList)
{
if (employee.Id == Id)
{
m_EmployeeList.Remove(employee);
}
}
}
}

public class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id;}}
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName =
value; } }
}
 
M

Marc Gravell

I would strongly recommend having collections as separate objects, for
instance:

public sealed class Company {
private readonly List<Employee> employees = new
List<Employee>();
public List<Employee> Employees { get { return employees; } }
}
public sealed class Employee { }

The question of whether to use a Collection<Employee>, a
List<Employee> or an EmployeeCollection is more complex; but unless
you have a need to tweak the add/remove rules, I'd start simply with a
List<Employee> as it is the most efficient (a Collection<Employee>
simply wraps an IList<T>, adding overhead).

The issue of the Company also acting (directly) as a collection of
Employee objects is that it breaks data-binding; data-binding assumes
that it is either a collection *xor* an item itself; if the Company is
itself an IList, you will never be able to bind to any properties of
the Company, as it will assume you want to bind to the same-named
property on the sub-tems of the collection (employees).

Marc
 
R

RSH

Marc,

Thanks for the reply!

So you are saying do away with the EmployeeCollections class and simply tuck
the collection in the company class?

That makes sense.

Thanks!
Ron
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,

Depending of your rules (like if you have more than noe company, if an
employee can be under two companies at teh same time, etc) you decide what
is the best approach.

One example I have, Order y OrderLineDetail (which is kind of similir to
your example)

class Order{
List<OrderDetail> details;
public ReadOnlyCollection<OrderDetail> GetDetails{ return new
ReadOnlyCollection<OrderDetail>( details);}

public OrderDetail AddDetail( .... );


Unlesss you have some pparticular need I would go with List<T> for the
collections.
 
R

RSH

Marc,

I n playing with the example you gave me, I noticed you made the collection
ReadOnly...so now I cant access the properties of the employee externally
(Client) which is a good thing, but I cant access the individual employee
properties inside the company Object either...bad thing. Lets say I give the
UI the ability to modify the Id base (instead of starting at 1, I want to
change it to 1001)...so I want to add 1000 to each employeeId. I cant do
that in a read only collection.

class Program
{
static void Main(string[] args)
{
Company company = new Company(0, "Bills House of Bugs");
company.PrintEmployees();
company.ChangeBaseId(1000);
company.PrintEmployees();
Console.Read();
}
}

public sealed class Company
{
private int m_Id;
private string m_Name;
private readonly List<Employee> m_EmployeeCollection = new
List<Employee>(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }
public List<Employee> Employees { get { return m_EmployeeCollection; } }

public void LoadEmployees()
{
Employee employee = null;
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
employee = new Employee(this, 1, "Thomas", "Fred");
m_EmployeeCollection.Add(employee);
employee = new Employee(this, 2, "Williams", "Pam");
m_EmployeeCollection.Add(employee);
}

public void PrintEmployees()
{
foreach (Employee employee in m_EmployeeCollection)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}", employee.Id,
employee.LastName, employee.FirstName));
}
}

public void ChangeBaseId(int BaseId)
{
foreach (Employee employee in m_EmployeeCollection)
{
employee.Id += BaseId; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Fails
}
}
}

public sealed class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id;}}
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName =
value; } }
}
 
M

Marc Gravell

Yes; unless EmployeeCollection is adding value in some way, don't
write it.

And if you do need it, consider subclassing Collection<Employee> and
overriding a few methods.

Marc
 
M

Marc Gravell

Marc,
I noticed you made the collection ReadOnly

No; I made the field "readonly", which simply stops you from
reassigning the collection (as opposed to changing the contents).

For info, the reason you can't change the Id is because you haven't
provided a setter - in which case the *property* is readonly.
public int Id { get { return m_Id;}}

Ignacio mentioned ReadOnlyCollection as a method return, which makes
life simple in a few scenarios, but limits the caller on altering the
list via the standard Add etc (which will prevent grid-binding from
adding etc) - however, each item in the collection is just a
reference, so even with a ReadOnlyCollection you should be able to
edit the individual Employee / Client properties.

The code (below) works fine editing properties of objects in a
ReadOnlyCollection.

Marc

using System.Collections.ObjectModel;
using System.Collections.Generic;
using System;
static class Program {
static void Main() {
Company corp = new Company();
foreach(Employee emp in corp.GetReadonly()) {
emp.Id += 1000;
}
foreach (Employee emp in corp.Employees) {
Console.WriteLine("{0}: {1}", emp.Id, emp.Name);
}
}
}
public sealed class Company {
private readonly Collection<Employee> employees = new
Collection<Employee>();
public Collection<Employee> Employees { get { return
employees; } }
public Company() {
employees.Add(new Employee { Id = 1, Name = "Fred" });
employees.Add(new Employee { Id = 2, Name = "Fred" });
employees.Add(new Employee { Id = 3, Name = "Fred" });
}
public IList<Employee> GetReadonly() {
return new ReadOnlyCollection<Employee>(Employees);
}
}
public sealed class Employee {
public int Id { get; set; }
public string Name { get; set; }
}
 
S

schneider

Also note, for public APIs it is recommended that you use the
System.Collections.ObjectModel namespace and not List<>.

Schneider
 
R

RSH

Thanks for all of the good info!

Now that I have changed over to System.Collections.ObjectModel namespace,
and I have modified the Collection to return a Readonly copy of the
colletion through the accessor, I cant modify the collection at the client
level (very good) but I can modify the Employee properties in the
collection. Since I need the Company Object to be able to modify the
Employee object I cant make the properties readonly, but what if I dont want
the Client to be able to make changes directly to the Employee objects
contained within the collection?


namespace Workshop
{
class Program
{
static void Main(string[] args)
{
Company company = new Company(0, "Bills House of Bugs");
company.PrintEmployees();
company.ChangeBaseId(1000);
company.PrintEmployees();
foreach (Employee employee in company.Employees)
{
employee.Id += 10000; // <<<<<<<<<<<<<<<<<<<<<<<<< I dont want the
client to be able to make changes to the Employee Objects directly
}
company.PrintEmployees();
Console.Read();
}
}

public sealed class Company
{
private int m_Id;
private string m_Name;
private Collection<Employee> m_EmployeeCollection = new
Collection<Employee>();

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }
public ReadOnlyCollection<Employee> Employees { get { return new
ReadOnlyCollection<Employee>(m_EmployeeCollection); } }

public void LoadEmployees()
{
Employee employee = null;
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
employee = new Employee(this, 1, "Thomas", "Fred");
m_EmployeeCollection.Add(employee);
employee = new Employee(this, 2, "Williams", "Pam");
m_EmployeeCollection.Add(employee);
}

public void PrintEmployees()
{
foreach (Employee employee in m_EmployeeCollection)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}", employee.Id,
employee.LastName, employee.FirstName));
}
}

public void ChangeBaseId(int BaseId)
{
foreach (Employee employee in m_EmployeeCollection)
{
employee.Id += BaseId; //<<<<<<<<<<<<<<<<<<<<<< I do however want the
company object to be able to make changes to the employee objects
}
}
}

public sealed class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id; } set { m_Id = value; } }
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName =
value; } }
}
}
 
M

Marc Gravell

I cant modify the collection at the client level (very good)

I'm not sure why that is /necessarily/ a good thing (although I can
see some use-cases); normally I expect to use "something.Items.Add".
It will also (as mentioned) impact databinding. If you want to
influence this step, then consider inheriting from Collection<T> and
overriding the methods you want to handle...

Marc
 
S

sloan

I do this.

1 interface for the business object.
1 interface for the collection.
1 concrete business object : interface
1 concrete collection : interface

// object interface
namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{

public interface IZebra : IComparable<IZebra>
{
Guid ZebraUUID { get; set; }
System.DateTime CreateDate { get; set; }
string ZebraName { get; set; }
int CompareTo(IZebra other, Comparers.ZebraType comparisonType);

}



}


// collection interface

namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{


public interface IZebraCollection : IList< IZebra >
{

void Sort();
void Sort(IComparer<IZebra> icomp);

}


}




// object concrete

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;
using MeasInc.Applications.ZooropaManager.Interfaces.Comparers ;


namespace MeasInc.Applications.ZooropaManager.BusinessLogic.BusinessObjects
{

[Serializable]
[DataContract] // WCF specific, you can remove
public class Zebra : IZebra
{
[DataMember] // WCF specific, you can remove

private Guid _zebraUUID;

[DataMember] // WCF specific, you can remove

private System.DateTime _createDate;

[DataMember] // WCF specific, you can remove

private string _zebraName;





public Zebra() { } //unnecessary, but a placeholder

#region IZebra Members

public Zebra(Guid zebraUUID)
{
this.ZebraUUID = zebraUUID;
}

public Zebra(Guid zebraUUID, string zebraName, System.DateTime
createDate)
{
this.ZebraUUID = zebraUUID;
this.CreateDate = createDate;
this.ZebraName = zebraName;

}


#endregion

#region IZebra Members

public Guid ZebraUUID
{
get { return _zebraUUID; }
set { _zebraUUID = value; }
}

public System.DateTime CreateDate
{
get { return _createDate; }
set { _createDate = value; }
}
public string ZebraName
{
get { return _zebraName; }
set { _zebraName = value; }
}


#endregion



#region IComparable<IZebra> Members

public int CompareTo(IZebra other)
{
return this.ZebraUUID .CompareTo(other.ZebraUUID );
}

#endregion






}

}




//concrete collection

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;

namespace MeasInc.Applications.ZooropaManager.BusinessLogic.Collections
{
[Serializable]
[CollectionDataContract] // WCF specific, you can remove

public class ZebraCollection : List<IZebra> , IZebraCollection
{
//yeah, that's it
}
}





................// stop cs code

You can actually get these code samples at:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!158.entry

and just kinda ignore the wcf stuff.



......

As far as creation, I highly recommend seperating the objects/collections
away from the code that creates them.
Use a Controller or Manager class for object / collection creation.

Which is also seen at the code sample above.



Try out my code... I think your EmployeeCollection may be more verbose than
you need.

Good luck.
 
S

sloan

I started out 2.0 code with a bunch of

List<SomeObject>

but since..have highly favored going to

public class SomeObjectCollection : List<SomeObject>
{}

and I'm future proofed, in case I ever need to write special methods for
just the collection.

..............




sloan said:
I do this.

1 interface for the business object.
1 interface for the collection.
1 concrete business object : interface
1 concrete collection : interface

// object interface
namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{

public interface IZebra : IComparable<IZebra>
{
Guid ZebraUUID { get; set; }
System.DateTime CreateDate { get; set; }
string ZebraName { get; set; }
int CompareTo(IZebra other, Comparers.ZebraType comparisonType);

}



}


// collection interface

namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{


public interface IZebraCollection : IList< IZebra >
{

void Sort();
void Sort(IComparer<IZebra> icomp);

}


}




// object concrete

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;
using MeasInc.Applications.ZooropaManager.Interfaces.Comparers ;


namespace
MeasInc.Applications.ZooropaManager.BusinessLogic.BusinessObjects
{

[Serializable]
[DataContract] // WCF specific, you can remove
public class Zebra : IZebra
{
[DataMember] // WCF specific, you can remove

private Guid _zebraUUID;

[DataMember] // WCF specific, you can remove

private System.DateTime _createDate;

[DataMember] // WCF specific, you can remove

private string _zebraName;





public Zebra() { } //unnecessary, but a placeholder

#region IZebra Members

public Zebra(Guid zebraUUID)
{
this.ZebraUUID = zebraUUID;
}

public Zebra(Guid zebraUUID, string zebraName, System.DateTime
createDate)
{
this.ZebraUUID = zebraUUID;
this.CreateDate = createDate;
this.ZebraName = zebraName;

}


#endregion

#region IZebra Members

public Guid ZebraUUID
{
get { return _zebraUUID; }
set { _zebraUUID = value; }
}

public System.DateTime CreateDate
{
get { return _createDate; }
set { _createDate = value; }
}
public string ZebraName
{
get { return _zebraName; }
set { _zebraName = value; }
}


#endregion



#region IComparable<IZebra> Members

public int CompareTo(IZebra other)
{
return this.ZebraUUID .CompareTo(other.ZebraUUID );
}

#endregion






}

}




//concrete collection

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;

namespace MeasInc.Applications.ZooropaManager.BusinessLogic.Collections
{
[Serializable]
[CollectionDataContract] // WCF specific, you can remove

public class ZebraCollection : List<IZebra> , IZebraCollection
{
//yeah, that's it
}
}





...............// stop cs code

You can actually get these code samples at:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!158.entry

and just kinda ignore the wcf stuff.



.....

As far as creation, I highly recommend seperating the objects/collections
away from the code that creates them.
Use a Controller or Manager class for object / collection creation.

Which is also seen at the code sample above.



Try out my code... I think your EmployeeCollection may be more verbose
than you need.

Good luck.





RSH said:
Hi,

I have a basic question around how to handle collections of objects when
using object composition.

In my sample below I have a typical arrangement, I have a Company class
and a Employee class. My question is whether I should create a collection
IN the company class to store the employees, or as in my sample below,
create a EmployeeCollection class which is responsible for managing the
collection of employees?

It seems like it is a better construct to have a seperate class to manage
the collection...am I wrong there?

Thanks,
Ron

public class Company
{
private int m_Id;
private string m_Name;
private EmployeeCollection m_EmployeeCollection = null;

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }

protected void LoadEmployees()
{
Employee employee = null;
m_EmployeeCollection = new EmployeeCollection(this);
// Load employees from datasource into EmployeeCollection object
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
}
}

public class EmployeeCollection
{
private List<Employee> m_EmployeeList = new List<Employee>();
private Company m_Parent = null;

public EmployeeCollection(Company Parent)
{
m_Parent = Parent;
}

public IEnumerable<Employee> GetEmployees()
{
foreach (Employee employee in m_EmployeeList)
{
yield return employee;
}
}

public void Add(Employee employee)
{
m_EmployeeList.Add(employee);
}

public void Remove(int Id)
{
foreach (Employee employee in m_EmployeeList)
{
if (employee.Id == Id)
{
m_EmployeeList.Remove(employee);
}
}
}
}

public class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string
FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id;}}
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName
= value; } }
}
 
S

Scott Roberts

The biggest problem I see with your example is that it calls LoadEmployees()
in the constructor of the Company class. If you follow this design pattern
throughout your application you will end up loading your entire database
into memory when you instantiate a top-level object.

It would probably be better to not load employees until the employees
collection is accessed for the first time. You can manage this from within
the Company class (in which case you could probably use a generic collection
for employees) or within the collection class itself (in which case you
would need a custom collection class).

Another thing to consider is, when you call
"myCompany.Employees.Add(someEmployee)" do you want the collection to
automatically set the company property for the employee being added? If so,
you'll need a custom collection class.
 
R

RSH

Sloan,

Im playing with the example you gave me and Im getting the exception:
Error 2 The type or namespace name 'Comparers' could not be found (are you
missing a using directive or an assembly reference?)

Thanks,
Ron


sloan said:
I do this.

1 interface for the business object.
1 interface for the collection.
1 concrete business object : interface
1 concrete collection : interface

// object interface
namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{

public interface IZebra : IComparable<IZebra>
{
Guid ZebraUUID { get; set; }
System.DateTime CreateDate { get; set; }
string ZebraName { get; set; }
int CompareTo(IZebra other, Comparers.ZebraType comparisonType);

}



}


// collection interface

namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{


public interface IZebraCollection : IList< IZebra >
{

void Sort();
void Sort(IComparer<IZebra> icomp);

}


}




// object concrete

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;
using MeasInc.Applications.ZooropaManager.Interfaces.Comparers ;


namespace
MeasInc.Applications.ZooropaManager.BusinessLogic.BusinessObjects
{

[Serializable]
[DataContract] // WCF specific, you can remove
public class Zebra : IZebra
{
[DataMember] // WCF specific, you can remove

private Guid _zebraUUID;

[DataMember] // WCF specific, you can remove

private System.DateTime _createDate;

[DataMember] // WCF specific, you can remove

private string _zebraName;





public Zebra() { } //unnecessary, but a placeholder

#region IZebra Members

public Zebra(Guid zebraUUID)
{
this.ZebraUUID = zebraUUID;
}

public Zebra(Guid zebraUUID, string zebraName, System.DateTime
createDate)
{
this.ZebraUUID = zebraUUID;
this.CreateDate = createDate;
this.ZebraName = zebraName;

}


#endregion

#region IZebra Members

public Guid ZebraUUID
{
get { return _zebraUUID; }
set { _zebraUUID = value; }
}

public System.DateTime CreateDate
{
get { return _createDate; }
set { _createDate = value; }
}
public string ZebraName
{
get { return _zebraName; }
set { _zebraName = value; }
}


#endregion



#region IComparable<IZebra> Members

public int CompareTo(IZebra other)
{
return this.ZebraUUID .CompareTo(other.ZebraUUID );
}

#endregion






}

}




//concrete collection

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;

namespace MeasInc.Applications.ZooropaManager.BusinessLogic.Collections
{
[Serializable]
[CollectionDataContract] // WCF specific, you can remove

public class ZebraCollection : List<IZebra> , IZebraCollection
{
//yeah, that's it
}
}





...............// stop cs code

You can actually get these code samples at:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!158.entry

and just kinda ignore the wcf stuff.



.....

As far as creation, I highly recommend seperating the objects/collections
away from the code that creates them.
Use a Controller or Manager class for object / collection creation.

Which is also seen at the code sample above.



Try out my code... I think your EmployeeCollection may be more verbose
than you need.

Good luck.





RSH said:
Hi,

I have a basic question around how to handle collections of objects when
using object composition.

In my sample below I have a typical arrangement, I have a Company class
and a Employee class. My question is whether I should create a collection
IN the company class to store the employees, or as in my sample below,
create a EmployeeCollection class which is responsible for managing the
collection of employees?

It seems like it is a better construct to have a seperate class to manage
the collection...am I wrong there?

Thanks,
Ron

public class Company
{
private int m_Id;
private string m_Name;
private EmployeeCollection m_EmployeeCollection = null;

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }

protected void LoadEmployees()
{
Employee employee = null;
m_EmployeeCollection = new EmployeeCollection(this);
// Load employees from datasource into EmployeeCollection object
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
}
}

public class EmployeeCollection
{
private List<Employee> m_EmployeeList = new List<Employee>();
private Company m_Parent = null;

public EmployeeCollection(Company Parent)
{
m_Parent = Parent;
}

public IEnumerable<Employee> GetEmployees()
{
foreach (Employee employee in m_EmployeeList)
{
yield return employee;
}
}

public void Add(Employee employee)
{
m_EmployeeList.Add(employee);
}

public void Remove(int Id)
{
foreach (Employee employee in m_EmployeeList)
{
if (employee.Id == Id)
{
m_EmployeeList.Remove(employee);
}
}
}
}

public class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string
FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id;}}
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName
= value; } }
}
 
S

sloan

Yeah, that's why I referenced the URL...to download the code.....

But here you go !

namespace MeasInc.Applications.ZooropaManager.Interfaces.Comparers
{
// Enumeration of comparison types
public enum ZebraType
{
ZebraUUID,
Name,
CreateDate
};



// Enumeration of sorder order types
public enum SortOrderType
{
Ascending,
Descending
};

}



Basically, you're saying...when I want to find if one zebra matches
another....I can pick the property via an Enum.
ZebraUUID OR Name OR CreateDate.
A little filter flexability.

...


As far a Company having Employees....and populating them...this is really
where I like the Controller/Manager class(es).

Take a look here:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!139.entry

get the code.
And look at my CustomerController, where I populate Customers, their
Order(s) and then the OrderDetails of the Order(s).

It shows how to use filter matches to populate 2 child collections.
And you're using very "clean" sql and IDataReaders to get all the work done
as well.

...






RSH said:
Sloan,

Im playing with the example you gave me and Im getting the exception:
Error 2 The type or namespace name 'Comparers' could not be found (are you
missing a using directive or an assembly reference?)

Thanks,
Ron


sloan said:
I do this.

1 interface for the business object.
1 interface for the collection.
1 concrete business object : interface
1 concrete collection : interface

// object interface
namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{

public interface IZebra : IComparable<IZebra>
{
Guid ZebraUUID { get; set; }
System.DateTime CreateDate { get; set; }
string ZebraName { get; set; }
int CompareTo(IZebra other, Comparers.ZebraType comparisonType);

}



}


// collection interface

namespace MeasInc.Applications.ZooropaManager.Interfaces.Zebra
{


public interface IZebraCollection : IList< IZebra >
{

void Sort();
void Sort(IComparer<IZebra> icomp);

}


}




// object concrete

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;
using MeasInc.Applications.ZooropaManager.Interfaces.Comparers ;


namespace
MeasInc.Applications.ZooropaManager.BusinessLogic.BusinessObjects
{

[Serializable]
[DataContract] // WCF specific, you can remove
public class Zebra : IZebra
{
[DataMember] // WCF specific, you can remove

private Guid _zebraUUID;

[DataMember] // WCF specific, you can remove

private System.DateTime _createDate;

[DataMember] // WCF specific, you can remove

private string _zebraName;





public Zebra() { } //unnecessary, but a placeholder

#region IZebra Members

public Zebra(Guid zebraUUID)
{
this.ZebraUUID = zebraUUID;
}

public Zebra(Guid zebraUUID, string zebraName, System.DateTime
createDate)
{
this.ZebraUUID = zebraUUID;
this.CreateDate = createDate;
this.ZebraName = zebraName;

}


#endregion

#region IZebra Members

public Guid ZebraUUID
{
get { return _zebraUUID; }
set { _zebraUUID = value; }
}

public System.DateTime CreateDate
{
get { return _createDate; }
set { _createDate = value; }
}
public string ZebraName
{
get { return _zebraName; }
set { _zebraName = value; }
}


#endregion



#region IComparable<IZebra> Members

public int CompareTo(IZebra other)
{
return this.ZebraUUID .CompareTo(other.ZebraUUID );
}

#endregion






}

}




//concrete collection

using MeasInc.Applications.ZooropaManager.Interfaces.Zebra;

namespace MeasInc.Applications.ZooropaManager.BusinessLogic.Collections
{
[Serializable]
[CollectionDataContract] // WCF specific, you can remove

public class ZebraCollection : List<IZebra> , IZebraCollection
{
//yeah, that's it
}
}





...............// stop cs code

You can actually get these code samples at:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!158.entry

and just kinda ignore the wcf stuff.



.....

As far as creation, I highly recommend seperating the objects/collections
away from the code that creates them.
Use a Controller or Manager class for object / collection creation.

Which is also seen at the code sample above.



Try out my code... I think your EmployeeCollection may be more verbose
than you need.

Good luck.





RSH said:
Hi,

I have a basic question around how to handle collections of objects when
using object composition.

In my sample below I have a typical arrangement, I have a Company class
and a Employee class. My question is whether I should create a
collection IN the company class to store the employees, or as in my
sample below, create a EmployeeCollection class which is responsible for
managing the collection of employees?

It seems like it is a better construct to have a seperate class to
manage the collection...am I wrong there?

Thanks,
Ron

public class Company
{
private int m_Id;
private string m_Name;
private EmployeeCollection m_EmployeeCollection = null;

public Company(int Id, string Name)
{
m_Id = Id;
m_Name = Name;
LoadEmployees();
}

public int Id { get { return m_Id;}}
public string Name { get { return m_Name; } set { m_Name = value; } }

protected void LoadEmployees()
{
Employee employee = null;
m_EmployeeCollection = new EmployeeCollection(this);
// Load employees from datasource into EmployeeCollection object
employee = new Employee(this, 0, "Smith", "Doug");
m_EmployeeCollection.Add(employee);
}
}

public class EmployeeCollection
{
private List<Employee> m_EmployeeList = new List<Employee>();
private Company m_Parent = null;

public EmployeeCollection(Company Parent)
{
m_Parent = Parent;
}

public IEnumerable<Employee> GetEmployees()
{
foreach (Employee employee in m_EmployeeList)
{
yield return employee;
}
}

public void Add(Employee employee)
{
m_EmployeeList.Add(employee);
}

public void Remove(int Id)
{
foreach (Employee employee in m_EmployeeList)
{
if (employee.Id == Id)
{
m_EmployeeList.Remove(employee);
}
}
}
}

public class Employee
{
private int m_Id;
private string m_LastName;
private string m_FirstName;
private Company m_Parent = null;

public Employee(Company Parent,int Id, string LastName, string
FirstName)
{
m_Id = Id;
m_LastName = LastName;
m_FirstName = FirstName;
m_Parent = Parent;
}

public int Id { get { return m_Id;}}
public string LastName { get { return m_LastName; } set { m_LastName =
value; } }
public string FirstName { get { return m_FirstName; } set { m_FirstName
= value; } }
}
 
S

sloan

Well, I actually defer this type of populating to a Controller/Manager
class.

The Controller/Manager controls populating the parent object(s) (Company)
and any children collections.

You can see it here:
http://sholliday.spaces.live.com/Blog/cns!A68482B9628A842A!139.entry

Download the code.
Find the CustomerController.

and see how I populate
Customer(s), all their Order(s) (as a child collection), and then all the
OrderDetails(of the Order(s)) as a second child collection.

...

I'm not voting against deferred population of objects (aka, don't get the
Employees for a Company until asked to), but it depends on design goals.

For the most part, I populate everything up front. But always subsets of
the overall database data.
(1 Customer, their Orders, the OrderDetails for example)...

So it good to know both ways.

I prefer to hit the db one time....use IDataReader(s) (and NextResultSet())
to populate my objects/child collections in one hit.
Get In, Get Out is my db line of thinking.

...
 
R

RSH

I have been playing around with sloan's example and it makes good sense.

I am still having trouble identifying where in my initial example that goes.
Does my employee collection reside in the company object, or should it
remain a seperate entity and then both the client and company can add
employee objects to the collection. When i initially designed the classes I
was attempting to encapsulate the collection within the company, because I
wanted all access to the employees to be handled through the company. I was
under the impression that was how object composition was supposed to take
place. But now Im just confused.

Ron
 
S

sloan

My suggestion:


public interface ICompany
{
Guid CompanyUUID{set;get;}
string CompanyName{set;get;}

IEmployeeCollection AllEmployees{set;get;}

}


public class Company : ICompany
{

IEmployeeCollection allEmployees = null;//
//or
// IEmployeeCollection allEmployees = new EmployeeCollection();//concrete
here

IEmployeeCollection AllEmployees
{
get{return _allEmployees;}
set{ _allEmployees=value;}

}

//I'll leave the other 2 properties to you..to not clutter up the example

}


Does an employee always belong to only one company?


Then you allow the Controller/Manager to do the populating.

Sometimes (lets say you're just editing the companyName...you want a
"shallow" copy of the data.
You don't want the AllEmployees populated...you just want to be able to edit
the name.

public class CompanyController
{
ICompany GetSingleShallow( Guid companyUUID )
{
//get an IDataReader...but a single ResultSet with only 1 company
info in it.
}

ICompany GetSingleWithEmployees( Guid companyUUID )
{
//get an IDataReader...but with 2 ResultSets
//first one is the basic company info
//second is the Employees under that Company
}
}


That's what I'm saying...the Controller gives you flexibiltiy for what you
want.
ICompany with No Employees.
ICompany with Employees

Your choice.

You don't want to poplulate "All Employees" if you're simply editing the
CompanyName......
see what I mean?


Check my blog, the 1.1 "Custom Objects" example...where I have the
CustomerController.

and how I'm using an IDataReader to populate the
Customer(s)
Order(s)
OrderDetail(s)
as object/childcollection/childcollection.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top