Lesson 4 - Birthday Reminder - Logic Layer
In the previous lesson, Birthday Reminder - Designing Forms, we designed all the forms for our application. In this tutorial, we're going to design the logical layer, that is, classes containing the application logic.
Person
We'll certainly work with people in our app, so let's create a class for
them. Be sure to put the public
modifier before the class name.
Properties
The person will have 2 properties: name and birthday. Name
will
be string
, Birthday
will be of the
DateTime
type. We're going to set these properties using a
parametric constructor.
public class Person { public string Name { get; set; } public DateTime Birthday { get; set; } public Person(string name, DateTime birthday) { Name = name; Birthday = birthday; } }
Methods
In addition to the constructor, the class will have
CalculateAge()
and RemainingDays()
methods.
CalculateAge()
The method calculates and returns the person's current age in whole years.
Unfortunately, this calculation isn't just about subtracting two dates, since
TimeSpan
can't determine the number of years, only the number of
days. To calculate the age, we'll follow these steps:
- We'll get the current date (without time) using
DateTime.Today
. - We'll calculate the age as the difference of the current date's and the birthday's years. You probably know that such age isn't accurate. If we were born on 2/1/1990 and today is 1/1/2010, we aren't 20 years old, but only 19. For this reason, we'll make a correction.
- If the current date is before the date of birth plus the years we've calculated, the above case has occurred and we have to reduce the age by one year.
- We'll return the final age we calculated.
The method's code looks as follows.
public int CalculateAge() { DateTime today = DateTime.Today; int age = today.Year - Birthday.Year; if (today < Birthday.AddYears(age)) age--; return age; }
RemainingDays()
This method returns how many days remain until the person's birthday. Here's how to find out:
- We'll get the current date (without time).
- We'll get the next birthday by adding age + 1 to the date of birth.
- We'll subtract the dates and return the total day difference. Since the
difference is of the
double
type, we have to convert it to theint
type.
public int RemainingDays() { DateTime today = DateTime.Today; DateTime nextBirthday = Birthday.AddYears(CalculateAge() + 1); TimeSpan difference = nextBirthday - DateTime.Today; return Convert.ToInt32(different.TotalDays); }
ToString()
Since we're going to list the people, we'll override the
ToString()
method to return the person's name:
public override string ToString() { return Name; }
Person Manager
The next logical component of the application will be a person manager. The class will take care of all the people, will be able to add them, remove them, and save their list into a file and reload it. And finally, it'll be able to find the person with the nearest birthday among all the people.
Create a PersonManager
class in the project and make it
public.
Properties and Attributes
The only public property of the class will be a list of the people. The list
will be of the BindingList
type. We haven't met this collection
type yet. It's a smarter List
that can trigger a change event when
its content changes. This mechanism automatically refreshes all the controls in
a form that have this BindingList
set as the data source. You can
imagine that refreshing dozens of controls in a form manually after every change
can be very confusing. Once we add a new person in our app, it'll be visible in
the people list immediately without us having to refresh it from the code, it'll
refresh itself. We'll initialize the BindingList
in the
constructor.
If we wanted to implement people editing, the Person
class would
need to implement the INotifyPropertyChanged
interface. Any change
(changing the name for example) would then be automatically reflected in all
controls of all forms where this person appears. However, we won't be doing this
here to keep things simple.
So far, the class looks like this:
public class PersonManager { public BindingList<Person> People { get; set; } public PersonManager() { People = new BindingList<Person>(); } }
Methods
In addition to adding and removing, the class will also be able to find the person with the nearest birthday. We'll discuss saving and loading people to/from a file later.
Add()
This method adds a new person to the BindingList
. Since we use a
form to add the person, it'll be useful if the method took the person's
properties as parameters and created a new instance based on those. From the
date of birth we'll only save the date part, without the time.
Before adding the person, we'll make sure the name isn't too short and the date entered isn't in future. If any of these situations occur, we'll throw an exception. Exceptions are the right way to handle errors in object-oriented applications.
Exceptions are explained in more detail in Working
with Files in C# .NET. If you haven't met those yet, you only need to know
that we throw an exception using the throw
keyword, followed by the
exception instance. There are several types of exceptions, and we can also
create our own. In our case, we can use ArgumentException
. We enter
the error message as a parameter of the exception constructor. Once the
exception is thrown, the method no longer continues. We're going to handle the
error later when we'll call the method from the form.
public void Add(string name, DateTime birthday) { if (name.Length < 3) throw new ArgumentException("Name is too short"); if (birthday.Date > DateTime.Today) throw new ArgumentException("Birthday must not be in future"); Person person = new Person(name, birthday.Date); People.Add(person); }
Remove()
This method removes a person from the BindingList
. Since we
always want to remove an already completed person, this method takes the person
instance as a parameter.
public void Remove(Person person) { People.Remove(person); }
FindNearest()
This method finds and returns the person with the nearest birthday. To find
the person in the collection, we'll use the LINQ OrderBy()
method to
order people by how many days remain until their birthday. We'll store the
result in a collection which type we're not going to specify and use the
var
keyword instead, as is customary with LINQ. Then we'll return
the first person. We should only call the method if there are people in the
collection. Although it should be clear from the code what the method does, you
can, of course, look at the tutorials in the Collections and LINQ course, where LINQ
is described in more detail.
public Person FindNearest() { var sortedPeople = People.OrderBy(p => p.RemainingDays()); return sortedPeople.First(); }
We'll continue in the next lesson, Birthday Reminder - Wiring the Presentation and Logic Layers, to get the app up and running. The current source code is available to download below.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 88x (717.23 kB)
Application includes source codes in language C#