Following on from this question Good class design by example I have a follow-up one.
I want to create some collections that are owned by other objects. To recap, I have a Person class and I want the Person to be able to have one or more Addresses. So I thought I would create an Address class and an Addresses collection. Make sense? Here's my code so far
class Person
{
public Person(int SSN, string firstName, string lastName)
{
this.SSN = SSN;
FirstName = firstName;
LastName = lastName;
}
public int SSN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Address
{
public Address(string line1, string postCode)
{
Line1 = line1;
PostCode = postCode;
}
public int ID { get; set; }
public string Line1 { get; set; }
public string PostCode { get; set; }
}
class Addresses : System.Collections.CollectionBase
{
public int Person { get; set; } // should this be of type Person?
public void Add(Address addy)
{
List.Add(addy);
}
public Address Item(int Index)
{
return (Address)List[Index];
}
public void Remove(int index)
{
List.RemoveAt(index);
}
}
How can I associate many addresses with a Person? I'd like to do something like this in Main:
Person p = new Person(123,"Marilyn","Manson");
Address a = new Address("Somewhere", "blahblah");
p.Addresses.Add(a);
I then want to be able to save the addresses to a database. Should I do Address.Save() or Addresses.Save() (or something else)?
How would I change my code to implement that? Thanks for looking.
If you want the addresses OWNED by a Person, the person Should include a Collection of Addresses
class Person
{
<all the stuff you have>
List<Address> Addresses;
}
Don't forget to new it in the constructor,
Then to add an address you just
Address addressInstance = new Address(){...};
PersonInstance.Addresses.Add( addressInstance );
Cal-
If the Person "owns" addresses, it should expose an Addresses property. This could be of type ReadonlyCollection<Address> - no need to create your own collection type.
public Person {
private List<Address> _addresses;
public ReadonlyCollection<Address> Addresses {
get { return _addresses.AsReadOnly(); }
}
public void AddAddress(Address address) {
_addresses.Add(address);
}
}
If you are thinking about good class design, think twice whether every property needs a setter. Maybe you want your object immutable in which case you would want private setters.
That "Save" would follow the "ActiveRecord" pattern. More usual, e.g. when using an ORM like NHibernate, your object crosses some boundary which states that it is stored. In NHibernate this is called a Session which takes care of saving the object according to available information.
Each address should Save(). Your Addresses could Save which would call the save on each of its children.
You'll probably want to have a baseclass which has an enumerable Status such that you'd have New, Modified, UnModified, and Deleted so that you can choose which part of your CRUD will be called. Some people split it up so you'd have booleans, isNew, isModified, isDeleted.
This depends on your architecture...but, for saving the address I would not use the Address object to do it. It's your POCO (Plain old CLR Object) and it shouldn't know about the DAL. Another class should handle CRUD (Creat Read Update Delete) operations and it should be the part of your business layer that interacts with the DAL. Maybe call it AddressComponent.cs? This is a Domain Model Pattern approach.
For the address collection you can just do as flq says and have it as a property...there's really no need to have another object called Addresses.cs unless you need extra logic.
I would use generics to derive List (or any other collection class)
// This class add just an Owner (parent) to the list
public class OwnedList<T> : List<T>
{
public Object Owner { set; get; }
public OwnedList(Object owner)
{
Owner = owner;
}
}
And just pass the the object itself in your class :
class Contact
{
public int Person { get; set; }
private OwnedList<Address> _Addresses;
public OwnedList<Address> Addresses
{
get
{
if (_Addresses == null)
{
_Addresses = new OwnedList<Address>(this);
}
return _Addresses;
}
set
{
_Addresses = value;
_Addresses.Owner = this;
}
}
}
Related
Hey guys I recently started learning C# using microsoft .Net Tutorials.
I'm stuck at "Classes and Objects" Lesson.
At the end of the each lesson we have a little "homework" to do.
I need to write a program that prints out addresses for people and companies using three different classes (Person, Copmany, Address). I need to add them to collection and loop through, printing out the address for each company or person.
Here is the LINK (at the end of the page there is an example how the program should look like.
I'm not really sure how am I suppose to achieve this. Can someone please help me understand. This is what I come so far:
public class Address
{
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public override string ToString()
{
return $"{StreetAddress}, {City}, {PostalCode}, {Country}";
}
public Address()
{
StreetAddress = "Main123";
City = "SomeTown";
State = "OH";
PostalCode = "12345";
Country = "United States";
}
}
public class Person : Master
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Address ShippingAddress { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
ShippingAddress = new Address();
}
public override string ToString()
{
return $"{FirstName} {LastName} {ShippingAddress}\n";
}
}
public class Company : Master
{
public string Name { get; set; }
public Address ShippingAddress { get; set; }
public Company(string name)
{
Name = name;
ShippingAddress = new Address();
}
public override string ToString()
{
return $"{Name} {ShippingAddress}\n";
}
}
public class Master
{
public static List<Master> data = new List<Master>();
public static void Coll(Master master)
{
data.Add(master);
data.ForEach(Console.WriteLine);
}
}
class MainClass
{
public static void Main(string[] args)
{
Person John = new Person("John", "Smith");
Master.Coll(John);
}
}
If you have already reached the end of your tutorial, then it means that you have read the section about Inheritance and Polymorphism.
Please read this section again carefully.
If your task is: printing out the address for each company or person
Have a look on what the tutorial says about the ToString method:
Note that you can override the ToString method on any class where it will make sense to display string data related to an instance of the class.
So you first step would be to give the class Address the ability to display it's content, by overriding the ToString method.
You second step would be to use inheritance and create a super class which can be inherited by your 2 classes Person and Company. As it is done in the tutorial with the class Shape.
This will allow you to to add them to [a single] collection (as you wrote in your request).
Try to combine these steps and if you still have questions drop me a comment.
(Please keep rereading the tutorial, all the necessary information is in there, you just have to combine the different parts). Good fortune!
EDIT:
After you have edited your code, the next steps would be to give your classes Person and Company the ability to display information about themselves. Meaning you should override the ToString method also there.
One tip : you can use the ToString method of the class Address to display the details of the ShippingAddress. No need to write the details again ;) If you include the variable in the extrapolated string $"... the ToString method will be called implicitly and display all the details:
$"{ShippingAddress}"
One problem with your Coll method is that it is using a local variable List<Master> data as storage. This variable will cease to exist when the compiler returns from the method. Another problem is that in each call you create a new List making it impossible to store more than 1 element. What you can do is to make it a static variable inside the Master class. So that every time you add an item it will stay in memory and can be accessed later on.
You can also make the method static since it does not really require an object of type Master to be called (it does not access any class properties)
public class Master
{
public static List<Master> data = new List<Master>();
public static void Coll(Master master)
{
data.Add(master);
data.ForEach(Console.WriteLine);
}
}
You can call it like this then:
Person John = new Person("John", "Smith");
Master.Coll(John);
This is fairly straight forward. You can do it using interfaces or polymorphism.
You need put objects into collection, and iterate through all of them.
entityList = new List<IPrintable>();
foreach(var entity in entityList)
{
entity.PrintAddress();
}
To achieve this, you need to use common interface for both classes;
internal interface IPrintable
{
void PrintAddress();
}
You need to implement this interface in both classes.
public class Person : IPrintable
{
public void Print()
{
Console.WriteLine("Person's address is {0}", ShippingAddress.ToString());
}
}
public class Company : IPrintable
{
public void Print()
{
Console.WriteLine("Company's is {0}", ShippingAddress.ToString());
}
}
public class Address
{
public string ToString()
{
return string.Format(" Street: {0}, {1}, {2}, {3}, post code{4}, {5}", StreetAddress, City, State, PostalCode, Country);
}
}
Instead of having common interface you can make abstract base class, which will hold ShippingAddress field and have virtual method Print() without implementation. Then this base class will be type you use in List class
var entitylist = newList();
public abstract class ShippableEntity
{
private Address _Address;
private ShippableEntity(Address address)
{
_Address = address;
}
public abstract void Print();
public Address ShippingAddress { get { return _Address; } }
}
Then you inherit this class in Person and Company and implement Print method in concrete classes.
Then you can do:
entitylist.Add(new Person(....));
entitylist.Add(new Company(....));
and do the same thing with foreach as with interface.
You need to call base class constructor!
public class Person : ShippableEntity
{
public Person(Address address)
: base(adress)
{
}
}
I hope this helps.
I'm new to OO design, and I wondering whether it is typical to have designs where objects contain lists of other objects. An example is below:
// Person object containing a list of phone numbers
public class Person
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Phone> Contacts { get; set; }
public void AddPhoneNumber(Phone phone)
{
Contacts.Add(phoneNumber);
}
}
// Phone object
public class Phone
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
// Example code to establish an object:
Person p1 = new Person();
p1.FirstName = "John";
p1.LastName = "Smith";
p1.AddPhoneNumber(new Phone() { Number = "555957000" });
p1.AddPhoneNumber(new Phone() { Number = "555579561" });
Is there a better way to structure this that simplifies the design and allows easier access to the data? Thanks!
Yes, it is absolutely fine to have an object contains list of object. In OOPs this is called Composition, which represent strong relationship between participating class.
I wondering whether it is typical to have designs where objects
contain lists of other objects.
Absolutely, this is normal as an object can contain lists that belong to only that particular object. One of many examples is when you're traversing a binary tree or such you could have each node have their own list which identifies their children. There are many more cases in which an object should/could contain their own list.
Going back to your code, you seem to have an error because the code below states that the list will contain Phone objects.
public List<Phone> Contacts { get; set; }
but yet you're passing in a string object rather than a phone object.
public void AddPhoneNumber(string phoneNumber)
{
Contacts.Add(phoneNumber); // this code shouldnt compile
}
rather what you can do is this:
public void AddPhoneNumber(string phoneNumber)
{
Contacts.Add(new Phone() { Number = phoneNumber });
}
There's nothing wrong with your code. It's perfectly valid to have a list of objects within an object.
Though adding a string to your List of Phone objects will throw an error.
You can add a phone number like so:
Person p1 = new Person();
p1.Contacts.Add(new Phone() { Number = "555579561" });
I am making a console test with C#.
Actually I have never used of C# but VB.Net. I want to create arrays for one-to-many relationship.
My one is 'A Student' has 'Name','Sex',...,'Courses Taken'.
A Student would take many course, each course has a Title and Included Subject. Each subject has Name, Description and Point.
Like this.
Student
- Name - Sex - Courses Taken
Take Courses
- Course Title - Subject Included
Subject
- Subject Name [Math] [MVC]
- Subject description [Advance] [Building Website]
- Subject Point [6.9] [5.6]
I want to store each entity in Arrays but I don't know how to connect subjects/courses to each Students. And how can I get Student who attending Math or MVC. Because every students can have more then more course/ more than one subjects.
You'll want to create classes to describe your different objects.
class Student
{
string Name { get; set; }
Gender Sex { get; set; } // write an enum for this
IEnumerable<Course> CoursesTaken { get; set; }
}
class Course
{
string Title { get; set; }
Subject Subject { get; set; }
}
class Subject
{
string Name { get; set; }
string Description { get; set; }
double Points { get; set; }
}
Using List to create enumerations of instances of these new types allow you to use LINQ to select or evaluate members of the list (nested for loops work as well):
// populate a list of students called studentList
//...
// use LINQ to select the students you want
var mathResults = studentList.Where(student => student.CoursesTaken.Any(course => course.Subject.Name == "Math"));
I feel like I've done with it in good way...
Pls check my code for my ques! ^^
I first made 3 classes as below..
class Students
{
public string StudentName;
public int StudSize;
public bool StudSex;
public List<Take_Courses> tcourses;
public Students() { }
public Students(string name, int size, bool sex, List<Take_Courses> tcourses)
{
StudentName = name;
StudSize = size;
StudSex = sex;
this.tcourses = tcourses;
}
}
and
class Take_Courses
{
public string classname;
public List<Arr_Courses> arr_Course;
public Take_Courses() { }
public Take_Courses(string classname, List<Arr_Courses> arr_courses)
{
this.classname = classname;
arr_Course = arr_courses;
}
}
class Arr_Courses
{
public string cosname;
public string cosdesc;
public float cospoint;
public Arr_Courses() { }
public Arr_Courses(string name, string description, float point)
{
cosname = name;
cosdesc = description;
cospoint = point;
}
}
I then initialized values in Main class as below;
Arr_Courses acos=new Arr_Courses();
Arr_Courses acos1=new Arr_Courses("Math","Advance Math1",9.5f);
Take_Courses cos=new Take_Courses();
Take_Courses cos_take1=new Take_Courses("Info Tech",new List<Arr_Courses>{acos1});
Students stu=new Students();
Students Stu1 = new Students("Milla", 22, true,new List<Take_Courses>{cos_take1});
I then make another List to be generated names of student and use for looping and assign each one to List.
I think some important part is this.
if (arr_stud[i].tcourses[j].arr_Course[k].cosname.Equals("Math"))
{
Math_Stud++;
MathStudents[i] = arr_stud[i];
}
I am sharing this if anyone needs something like this. Any ungraded codes is appreciated to be shared. Thanks so so.
I'm stuck in my inheritances bloating here:
First let me explain the premise of my problem.
My Model:
public class Person
{
[Key]
public int PersonId { get; set; }
[MaxLength(100)]
public string Name { get; set; }
}
public class SuperHero:Person
{
[MaxLength(100)]
public string SuperHeroName { get; set; }
public virtual ICollection<SuperPower> SuperPowers{ get; set; }
}
Now, I am trying to create my viewModels for my MVC website, I have those base classes that need to be inherited by all other viewmodel displaying/editing a Person/SuperHero:
public class BasePersonViewModel
{
public string Name { get; set; }
ctors()
}
public class BaseSuperHeroViewModel : BasePersonViewModel
{
public List<string> SuperPowers{ get; set; }
ctors()
}
Here is where I am stuck, I am trying to define only one ViewModel that could be used regarless of the base class and access property of Person and/or SuperHero (if the Person is a superhero). I've been pulling my hair out but so far only found a solution which i don't like:
Example:
public class SomeViewModel<T> where T : BasePersonViewModel
{
public BasePersonViewModel obj;
public DateTime BirthDate { get; set; }
public SomeViewModel(Person data) //: base(data)
{
if (data is SuperHero)
obj = new BaseSuperHeroViewModel (data);
else
obj = new BasePersonViewModel(data);
}
}
While this would work it's really not sexy to use. And on top of that, I could have another ViewModel that inherit from SomeViewModel as well.
Is there a cleaner way to achieve this?
Edit
My main goal is to be able to able to cast my SomeViewModel depending on the one of the baseclass. Let's say do something like in my Controller:
if myclass is SomeViewModel (of type SuperHero)
Exactly how you do it for Person/SuperHero db retrival/check
var data = context.Person.first(w=> w.Id==1)
if (data is SuperHero)
..
I would like this because I would like to use the same viewmodel let's say to list superhero and person, and just display slightly differently if it's a superhero
Edit 2
I was trying to avoid using the whole Model.Obj to be able to see it directly with the Model... But the more i think about it, the more I think this is not possible really... On top of that I would like to extend some other superHero specific properties in SomeViewModel (only if SomeViewModel is a superhero), that are not declared in the BaseSuperHeroModel one... Let's say in SomeViewModel I want the field 'ComesFromPlanet' only if superhero.
Edit 3
I thought about another way to do it, but it obviously creating various ViewModel.
For the most general case (all fields that are shared for all Person) I would keep my base:
public class BasePersonViewModel
{
public string Name { get; set; }
ctors()
}
I interface specific Person:
public Interface IBaseSuperHero
{
[MaxLength(100)]
public string SuperHeroName { get; set; }
public List<string> SuperPowers{ get; set; }
}
I would keep as well OtherViewModel like this:
public class SomeViewModel:BasePersonViewModel
{
Public datetime Birthdate {get;set;}
}
Then I would create a specific SomeviewModel for other Person inheritant and used interfaces to have old and new properties.
For example:
public class SomeViewModelSuperHero:SomeViewModel, IBaseSuperHero
{
public string OriginalPlanet {get;set;}
}
Is this a clean solution?
Sorry I'm sure I am not clear about this, but I try !
Thanks for your input and time.
I am trying to define only one ViewModel that could be used regarless of the base class and access property of Person and/or SuperHero (if the Person is a superhero)
Assuming you'd return default values for super-hero properties when the model is not a super-hero, you could do something like this:
public class PersonOrSuperHeroViewModel {
private Person person;
private SuperHero superHero;
public PersonOrSuperHeroViewModel(Person personOrSuperHero) {
if (personOrSuperHero is SuperHero) superHero = personOrSuperHero;
person = personOrSuperHero;
}
public IsSuperHero { get { return superHero != null; } }
... // super-hero properties only work when IsSuperHero == true
}
How about something like
public class Person {
public virtual BasePersonViewModel MainViewModel {
get { return new BasePersonViewModel(this);}
}
}
public class SuperHero : Person {
public override BasePersonViewModel MainViewModel {
get { return new BaseSuperHeroViewModel(this);}
}
}
So if all your people classes override the MainViewModel property to return the appropriate view, you don't need
public BasePersonViewModel obj;
public SomeViewModel(Person data) {
if (data is SuperHero)
obj = new BaseSuperHeroViewModel (data);
else
obj = new BasePersonViewModel(data);
}
Because you can have
public BasePersonViewModel obj;
public SomeViewModel(Person data) { obj = data.MainViewModel; }
which will work however many subclasses of person you have.
I have a customer hierarchy like so:
abstract class Customer {
public virtual string Name { get; set; }
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
When I retrieve a Customer, I would like to show on the web form the properties to edit/modify. Currently, I use if statements to find the child customer type and show the specialized properties. Is there a design pattern (visitor?) or better way so I can avoid the "if" statements in presentation layer? How do you do it?
Further information: This is an asp.net website with nHibernate backend. Each customer type has its own user control on the page that I would like to load automatically given the customer type.
Can you use reflection to get the list of properties specific to an subclass (instance)? (Less error-prone.)
If not, create a (virtual) method which returns the special properties. (More error prone!)
For an example of the latter:
abstract class Customer {
public virtual string Name { get; set; }
public virtual IDictionary<string, object> GetProperties()
{
var ret = new Dictionary<string, object>();
ret["Name"] = Name;
return ret;
}
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Max spending"] = MaxSpending;
return ret;
}
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Award"] = Award;
return ret;
}
}
You probably want to create sections (fieldsets?) on your Web page, anyway, so if would come into play there, making this extra coding kinda annoying and useless.
I think a cleaner organization would be to have a parallel hierarchy of display controls or formats. Maybe use something like the Abstract Factory Pattern to create both the instance of Customer and of CustomerForm at the same time. Display the returned CustomerForm instance, which would know about the extra properties and how to display and edit them.
new:
interface CustomerEdit
{
void Display();
}
edit:
abstract class Customer {
protected CustomerEdit customerEdit; // customers have an object which allows for edit
public virtual string Name { get; set; }
public void Display() { customerEdit.Display(); } // allow the CustomerEdit implementor to display the UI elements
}
// Set customerEdit in constructor, tie with "this"
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
// Set customerEdit in constructor, tie with "this"
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
usage:
Customer whichCouldItBe = GetSomeCustomer();
whichCouldItBe.Display(); // shows UI depeneding on the concrete type
Have you tried something like this:
public class Customer<T>
where T : Customer<T>
{
private T subClass;
public IDictionary<string, object> GetProperties()
{
return subClass.GetProperties();
}
}
With a subclass of:
public class FinancialCustomer : Customer<FinancialCustomer>
{
}
This is off the top of my head so might not work. I've seen this type of code in CSLA.NET.
Here's the link to the CSLA.NET class called BusinessBase.cs which has a similar definition to what I've given above.