Question

I have a class:

    public class Person
{
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string Email { get; private set; }
    public string Telephone { get; private set; }
    public Address Address { get; private set; }

    public Person(string firstName, string lastName)
    {
        //do null-checks
        FirstName = firstName;
        LastName = lastName;
        Address = new Address();
    }

    public void AddOrChangeEmail(string email)
    {
        //Check if e-mail is a valid e-mail here
        Email = email;
    }

    public void AddOrChangeTelephone(string telephone)
    {
        //Check if thelephone has correct format and valid symbols
        Telephone = telephone;
    }

    public void AddOrChangeAdress(Address address)
    {
        Address = address;
    }

The properties that are not in the constructor are optional, i.e. the person does not need an e-mail, address or telephone. However, I want to give the user of the class an opportunity to create the object without having to first provide the required information and then afterwards have to find out what methods to use to add the information.

Questions:

  1. Is it right to create 3 additional overloads to give them that option?
  2. Should i allow public setters on the optional properties and do the validation there?
  3. If the person marries and changes last name, do I need additional method to change the last name or should I make this setter public too, and just require them in constructor?
Was it helpful?

Solution

  1. No.
  2. Yes
  3. Make it public.

Assuming you are going to have more code in the AddOrChange methods such as formatting logic or validation then I'd do the following. Otherwise, I'd completely get rid of the AddOrChange methods:

public class Person
{
    private string _email = string.empty;
    private string _telephone = string.empty;
    private Address _address = new Address();

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { 
        get { return _email }
        set { AddOrChangeEmail(value); }
    }
    public string Telephone { 
        get { return _telephone;}
        set { AddOrChangeTelephone(value); }
     }
    public Address Address { 
        get { return _address;  }
        set { AddOrChangeAddress(value); }
    }

    public Person(string firstName, string lastName)
    {
        //do null-checks
        FirstName = firstName;
        LastName = lastName;
    }

    private void AddOrChangeEmail(string email)
    {
        //Check if e-mail is a valid e-mail here
        _email = email;
    }

    private void AddOrChangeTelephone(string telephone)
    {
        //Check if thelephone has correct format and valid symbols
        _telephone = telephone;
    }

    private void AddOrChangeAddress(Address address)
    {
        _address = address;
    }
}

To work with this class you could do any of the following:

Person p = new Person("Tom", "Jones");
p.Telephone = "9995551111";

or

Person p = new Person("Tom", "Jones") { Telephone = "9995551111", Email="spamme@ms.com" }

OTHER TIPS

AddOrChange is equivalent to simple property with public setter, thus you don't need those methods.

public class Person
{
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public Email Email { get; set; }
    public Telephone Telephone { get; set; }
    public Address Address { get; set; }

    public Person(string firstName, string lastName)
    {
        //do null-checks
        FirstName = firstName;
        LastName = lastName;
    }
}
  1. If user during person creation want to provide something additional beside required data, she can use class initializers. Also you can add some optional parameters to constructor.

    var bob = new Person("Bob", "Uncle") { Address = someAddress };

  2. If its OK for person to relocate, then why not to use public setter for changing address? Of course, you should check if address is valid. Also if relocating is a business process (i.e. you are relocating someone in hotel) then it would be nice to have this operation on domain service (which will verify if destination room is empty and ready).

  3. Allowing to change name is OK. Usually name is not identity for such entities, thus it could change.

Also, I'd introduced value objects for email and telephone. I think it is not person's responsibility to verify if email address valid. Move that to Email class. Same with Phone and Address.

Is lots of add/change methods and constructor overloads a consequence of DDD?

No, lots of updating methods is not consequence of DDD.

Code

Your Person class can be rewritten to have only 2 updating methods:

class Person

    public function Rename(FirstName as Name, LastName as Name) as Person

    public function ChangeContacts(
        Address as Maybe(of Address), 
        Phone as Maybe(of Phone), 
        Mail as Maybe(of MailAddress)) as Person
end class

Rename method accepts two required parameters of special Name type. Validation checks for names happen when names are created, not when they are passed into Person class.

ChangeContacts method accepts three optional parameters any of which can be absent. Special Maybe type indicates that they are optional. Special Address, Phone and MailAddress types indicate that these parameters are already valid and there is no need to validate them again in Person class.

Use case

Person marries and changes last name

Person = Person.
    Rename(Person.FirstName, LastNameAfterMarriage)

Person buys new phone number

Person = Person.
    ChangeContacts(Person.Address, NewPhoneNumber, Person.Mail)

Person lost phone number

Dim NewPhoneNumber = Maybe.Create(Nothing)
Person = Person.
    ChangeContacts(Person.Address, NewPhoneNumber, Person.Mail)

The pattern is to call updating method with old values + some new values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top