Help with class design

  • Thread starter Thread starter JSheble
  • Start date Start date
J

JSheble

I come from a Delphi background, and am currently trying to port or convert
some of our Delphi classes to C#. I've got a good handle on basic class
design, but am a bit lost with some of the more advanced things I need to
do, and was hoping somebody could point me to an online resource that can
help me better understand what I need to do.

For example, one of the classes is a Shipment class. A Shipment can have
any number of Packages, so I have designed a Shipment Class and a Package
class. I should be able to retrieve any of the packages by it's index or I
should be able to use a single package as a property. For example (these
examples are not working code, merely pseudo-code to demonstrate what I
need):

oShip = new Shipment();
oShip.AddPackage(sPackageID, dPackageWeight);
oShip.AddPackage(sDifferentPackageID, dPackageWeight);

// access the first package object in the shipment
MessageBox.Show(oShip.Packages[0].PackageID);

// loop though all packages
foreach(Package oPack in oShipment)
{
MessageBox.Show(oPack.PackageID);
}

Either a basic template that shows how I need to design my class or a good
online resource would be greatly appreciated!

TIA!
 
J... The Shipment class probably should not expose its internal data
like that. In other words, I would not allow an external caller to
access the Package collection in the Shipment directly. I would add a
level of indirection here, Properties for instance. Internally, the
Shipment class can store the Packages in a private array or in a
private, type safe collection derived from collection base. Externally,
the Shipment can provide get accessors (by index and/or key) or return a
read only collection or a copy of the internal array so the caller has
access to the individual packages but not to the private internal
collection in the shipment object. You can then add set accessors (Add,
Remove) that validate any attempt to modify the contents of the internal
collection.

Regards,
Jeff
// loop though all packages
foreach(Package oPack in oShipment)
{
MessageBox.Show(oPack.PackageID);
}
 
I come from a Delphi background, and am currently trying to port or convert
some of our Delphi classes to C#. I've got a good handle on basic class
design, but am a bit lost with some of the more advanced things I need to
do, and was hoping somebody could point me to an online resource that can
help me better understand what I need to do.

There really is no difference in how you would design classes in C# as
opposed to Delphi. If you got the design right in Delphi, then it should be
fine in C# :-)
For example, one of the classes is a Shipment class. A Shipment can have
any number of Packages, so I have designed a Shipment Class and a Package
class. I should be able to retrieve any of the packages by it's index or I
should be able to use a single package as a property. For example (these
examples are not working code, merely pseudo-code to demonstrate what I
need):

You need to start off by asking yourself whether a Package can exist outside
of a Shipment e.g. can a Package be stored in a Warehouse or transported in
a DeliveryVehicle.

If a Package can exist outside of a Shipment then the relationship between
them is one of Aggregation and can be expressed thus :

class Package
{
...
}

class PackageList
{
void Add(Package item)...
void Remove(Package item)...
Package this[int idx]...
...
}

class Shipment
{
PackageList Packages
{
get {...}
set {...}
}
}

But if the Packages cannot exist outside of a Shipment then the relationship
is one of Composition and should be expressed thus :

class Package
{
...
}

class Shipment
{
Package AddPackage()...
void RemovePackage(Package item)...
int PackageCount
{
get {...}
}...
PackageEnumerator GetPackageEnumerator()...
...
}

The essential difference is that the Composition relationship should not
allow adding or otherwise manipulating Packages without going through
methods of the Shipment.
oShip = new Shipment();
oShip.AddPackage(sPackageID, dPackageWeight);
oShip.AddPackage(sDifferentPackageID, dPackageWeight);

PAckageWeight should be a property of the Package class and you should not
need to pass such details to the Add method of the Shipment class. Assuming
you are using Composition, you should retrieve a new instance of the Package
class from the Shipment and set properties like Weight, etc on that
instance. I suspect though that you should be using Aggregation and
therefore you should create an instance of Package, set the properties
either in the constructor or otherwise, and then add the instance to the
Shipment.
// loop though all packages
foreach(Package oPack in oShipment)
{
MessageBox.Show(oPack.PackageID);
}

I am not sure whether you really should make the Shipment 'enumerable', that
implies it not really much more than a list ??

Joanna
 
Ok, so assuming a package cannot exist outside of a shipment, I would be
using the Composition type. In reality, a package could exist outside of a
shipment in the real world, but for this object model, it makes no sense to
have a package without a shipment.

But this line of pseudo-code you added:

PackageEnumerator GetPackageEnumerator()...

makes no sense to me. I'm assuming I will have to create a
PackageEnumerator type? Which should have or do what exactly?

I don't suppose you'd have a small example class showing or demonstrating
this, would you? Once I see and digest an example, hopefully I'd be able to
simulate it for my specific needs.
PAckageWeight should be a property of the Package class and you should not
need to pass such details to the Add method of the Shipment class.
Assuming
you are using Composition, you should retrieve a new instance of the
Package
class from the Shipment and set properties like Weight, etc on that
instance.

Actually, a package cannot exist without a weight, which is why I'm adding
it to the Addpackage method. A package must have a package ID and a weight.
If not, there's no sense in adding a package. I think leaving the weight as
a parameter is a feasible solution.

Joanna Carter (TeamB) said:
I come from a Delphi background, and am currently trying to port or convert
some of our Delphi classes to C#. I've got a good handle on basic class
design, but am a bit lost with some of the more advanced things I need to
do, and was hoping somebody could point me to an online resource that can
help me better understand what I need to do.

There really is no difference in how you would design classes in C# as
opposed to Delphi. If you got the design right in Delphi, then it should
be
fine in C# :-)
For example, one of the classes is a Shipment class. A Shipment can have
any number of Packages, so I have designed a Shipment Class and a Package
class. I should be able to retrieve any of the packages by it's index or I
should be able to use a single package as a property. For example (these
examples are not working code, merely pseudo-code to demonstrate what I
need):

You need to start off by asking yourself whether a Package can exist
outside
of a Shipment e.g. can a Package be stored in a Warehouse or transported
in
a DeliveryVehicle.

If a Package can exist outside of a Shipment then the relationship between
them is one of Aggregation and can be expressed thus :

class Package
{
...
}

class PackageList
{
void Add(Package item)...
void Remove(Package item)...
Package this[int idx]...
...
}

class Shipment
{
PackageList Packages
{
get {...}
set {...}
}
}

But if the Packages cannot exist outside of a Shipment then the
relationship
is one of Composition and should be expressed thus :

class Package
{
...
}

class Shipment
{
Package AddPackage()...
void RemovePackage(Package item)...
int PackageCount
{
get {...}
}...
PackageEnumerator GetPackageEnumerator()...
...
}

The essential difference is that the Composition relationship should not
allow adding or otherwise manipulating Packages without going through
methods of the Shipment.
oShip = new Shipment();
oShip.AddPackage(sPackageID, dPackageWeight);
oShip.AddPackage(sDifferentPackageID, dPackageWeight);

PAckageWeight should be a property of the Package class and you should not
need to pass such details to the Add method of the Shipment class.
Assuming
you are using Composition, you should retrieve a new instance of the
Package
class from the Shipment and set properties like Weight, etc on that
instance. I suspect though that you should be using Aggregation and
therefore you should create an instance of Package, set the properties
either in the constructor or otherwise, and then add the instance to the
Shipment.
// loop though all packages
foreach(Package oPack in oShipment)
{
MessageBox.Show(oPack.PackageID);
}

I am not sure whether you really should make the Shipment 'enumerable',
that
implies it not really much more than a list ??

Joanna
 
The package collection has to be available through the Shipment object.
Accessing it via oSHipment.Packages[iPkg].SomePackageProperty is the
desirable method for accessing individual packages or through an enumerator.
 
I see. My bad.
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Package p1= new Package(21);
Package p2= new Package(35);
Shipment s1= new Shipment();
s1.Add(p1);
s1.Add(p2);
for (int i=0; i<s1.Count;i++)
{
System.Console.Write(s1.GetPackage(i).ID+":");
System.Console.WriteLine(s1.GetPackage(i).Weight);
}
foreach(Package p in s1)
{
System.Console.Write(p.ID+":");
System.Console.WriteLine(p.Weight);
}
System.Console.ReadLine();
}

public class Package : IPackage
{
// static stuff
private static int uniqueID= 0;
private static int GetUniqueID()
{
lock(typeof(Package))
{
return uniqueID++; // returns zero at start
}
}

private int weight= 0;
private int id;

public Package(int weight)
{
this.id= Package.GetUniqueID();
if (weight > 0)
{
this.weight= weight;
}
}
#region IPackage Members
...
Regards,
Jeff
 
Ok, so assuming a package cannot exist outside of a shipment, I would be
using the Composition type. In reality, a package could exist outside of a
shipment in the real world, but for this object model, it makes no sense to
have a package without a shipment.

I still have my doubts about using Composition, but for the sake of example,
let's continue...
But this line of pseudo-code you added:

PackageEnumerator GetPackageEnumerator()...

makes no sense to me. I'm assuming I will have to create a
PackageEnumerator type? Which should have or do what exactly?

This just needs to implement IEnumerator; two methods : Reset(), MoveNext()
and one property : Current.
Actually, a package cannot exist without a weight, which is why I'm adding
it to the Addpackage method. A package must have a package ID and a weight.
If not, there's no sense in adding a package. I think leaving the weight as
a parameter is a feasible solution.

Assuming you are using Composition, then your method that takes two
parameters would be sensible.

Joanna

--
Joanna Carter (TeamB)

Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
 
Back
Top