Welcome to the first episode of my course “Becoming a software developer”, which is a starting point for your journey into the software development world. It also begins a first part (there will be 4 parts in total containing 4 episodes each) which is dedicated to the core concepts of object-oriented programming using the C# language.
All of the materials including videos and sample projects can be downloaded from here.
In case you’ve got here somehow randomly and have no clue what the heck is going, please read & watch the course introduction first.
Scope
In this very first episode, we’re talking about the following topics:
- Q&A: .NET vs .NET Core, CLR.
- Designing proper classes (encapsulation).
I do realize that at first I wanted to talk about other things such as:
- Inheritance.
- Polymorphism.
- Interfaces.
- Why we should strive for abstractions.
Yet it took me over 40 minutes to go through some good practices of the encapsulation therefore, all of the remaining topics mentioned above will be discussed in the second episode.
On the other hand, I do assume that you’ve gathered by now a basic knowledge of the C# language, which means that you should have an idea how to write a simple code (variables, loops, methods), declare a class, instantiate a new object etc.
Abstract
Classes are probably the most important part of the object oriented programming. They are like a blueprint of the things and entities around you. Thanks to the classes you can describe such entity using properties and allow for interaction with it via methods.
Whenever you design a class (or model actually) make sure that you make use of the available access modifiers (private, protected, public) which applies both to its properties
and the methods. Consider for example the User – most likely you would not want anyone to change it’s email e.g. to be empty or invalid. Then why would you use a public setter, instead of a private one?
1 2 3 4 5 6 7 8 9 10 11 |
public class BadUser { public string Email { get; set; } public string Password { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } public bool IsActive { get; set; } public DateTime UpdatedAt { get; set; } public decimal Funds { get; set; } } |
And if such user must have an email, it’s probably a good idea to make a constructor that takes an email as input parameter and skip the default one with no parameters. It’s a very trivial example, but that’s the way you have to start thinking in order to model the proper classes of your application domain. Otherwise, you will run into problems – not using access modifiers correctly or at all is one of the biggest mistakes. You have to think about a real entity (e.g. the car) and what it allows you to do (public methods), what should be hidden from the car owner (private methods), what state it may have (e.g. car is running by invoking Start() method), what are the validation rules (e.g. there’s no fuel – Start() will throw an exception) and so on. All of these patterns are labeled as encapsulation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
//Full source code is available in the sample project. public class GoodUser { public string Email { get; private set; } public string Password { get; private set; } //Let's assume that we don't care about the value here. public string FirstName { get; set; } //And the same way of thinking does apply here. public string LastName { get; set; } public int Age { get; private set; } public bool IsActive { get; private set; } public DateTime UpdatedAt { get; private set; } public decimal Funds { get; private set; } public GoodUser(string email, string password) { SetEmail(email); SetPassword(password); } public void SetEmail(string email) { if(string.IsNullOrWhiteSpace(email)) { throw new Exception("Email is incorrect."); } if(Email == email) { return; } Email = email; Update(); } public void SetPassword(string password) { if(string.IsNullOrWhiteSpace(password)) { throw new Exception("Password is incorrect."); } if(Password == password) { return; } Password = password; Update(); } public void IncreaseFunds(decimal funds) { if(funds <= 0) { throw new Exception("Funds must be greater than 0."); } Funds += funds; Update(); } public void Activate() { if(IsActive) { return; } IsActive = true; Update(); } public void Deactivate() { if(!IsActive) { return; } IsActive = false; Update(); } private void Update() { UpdatedAt = DateTime.UtcNow; } } |
For sure, there are use cases for classes being just a bag of public getters and setters (so called anemic classes) and that would be some special types like DTO (Data Transfer Object), ViewModels or database table mappings, but they have a specific use and are not part of the system core.
Resources
- https://www.pluralsight.com/courses/object-oriented-programming-fundamentals-csharp
- https://www.pluralsight.com/blog/software-development/understanding-object-oriented-programming-in-c
- http://www.introprogramming.info/english-intro-csharp-book/read-online/chapter-20-object-oriented-programming-principles/
- http://www.slideshare.net/TelerikAcademy/highquality-classes-and-class-hierarchies-best-practices-in-the-objectoriented-design
- http://www.dofactory.com/reference/csharp-coding-standards
- https://channel9.msdn.com/Series/C-Sharp-Fundamentals-Development-for-Absolute-Beginners/Understanding-and-Creating-Classes-14
Next
In the next episode, we’ll finish our journey with classes, interfaces and abstractions in general (at its core) by talking about the following features:
- Inheritance.
- Polymorphism.
- Interfaces.
- Why we should strive for abstractions.
As usual, make sure that you’ll do your own research first about the stuff listed above.
Pingback: Becoming a software developer – episode I – Patryk Huzarski | Personal Blog
I have a small remark, if you use DateTime class then testing
private void Update()
{
UpdatedAt = DateTime.UtcNow;
}
may be difficult. Maybe it will be better to use some singleton wrapper that can be stubbed in unit test e.g. MyDateTime.UtcNow that provides MyDateTime.SetUtcNowForUnitTesting(…).
Sometime people even use separate service like IDateTimeService. What do you think about this approach?
Of course this has less importance for beginning programmers and your course is awesome 🙂
Hi Marcin,
Sure, you could use e.g. a simple interface that contains a Func<DateTime> property or use NodaTime etc. but it would be way too much for this example (including testing) which purpose was to show why this equality comparison inside a SetX() method may be sometimes useful :).
Thank you!
Pingback: Dew Drop - January 26, 2017 (#2408) - Morning Dew
Congratulations on making your first course! I think many of us want to record a course, but only a few really make it happen.
While I’m not the target audience (since I already am a developer), it looks like a piece of quality work. I’d personally prefer it recorded on a smaller resolution, but I’m sure you’ll consider feedback coming from people and find out what works best 🙂
Hi Buli,
Thank you! Well, I think in the future episodes even the more advanced developers may find here something useful :). The problem with resolution is, that I have only 34″ available – 22″ is at my office and somehow I can’t record a screencast on my laptop directly as there are some artifacts on the screen. Therefore, the only solution for me was to increase the font size.
Hi, why do you use in classes constructor
Email = email
instead of
this.Email = Email
?
Hi Lukas,
I don’t see any point in using this keyword in that case, neither it adds any value nor increases the readability. Event the refactoring tools like R# would suggest eliminating this :).
Pingback: Becoming a software developer – episode II | Piotr Gankiewicz
Lets suppose that we do not want to change neither a name nor a surname after an account has been created. Would it be a proper way to write it like that:
public string LastName { get; private set; }
//….
public GoodUser(string email, string password, string lastName)
{
SetEmail(email);
SetPassword(password);
LastName = lastName;
//…
}
One could possibly add a method – private void SetLastName(string lastName) – just to have some kind of validation, but no one could use this method from outside the class? Is that right?
Because, as I understood, the method SetPassword allow to change the password at some time later on if wanted?
You should only have a { get; } without the set (even private), it would make the User class truly immutable (once created, can not be changed anymore).
The SetPassword() allows to change it later on, but for example, if you only want to assign a value during the creation of the new object and never be able to change it (even internally), you should remove the “setter”.
What’s the advantage of:
public string Email { get; private set; }
public void SetEmail(string email)
{
//setter code
}
versus:
private string _email;
public string Email
{
get { return _email; }
set{ //setter code }
}
if there’s any? Is it just a way to avoid backing field or is it considered more readable? Let’s assume that you need to create a custom getter anyway. Would you create a separate methods for both getter and setter?
It was explained in the video. Basically, I want to explicit about assigning values to the properties. If there’s an underlying email validation that can throw an exception or another field gets also updated (e.g. UpdatedAt) etc. I personally find it “weird” to use a regular setter and for example get an exception out of nowhere. In my opinion, me the setter should be only used only for a property that can accept literally any value and has no influence on the other properties of the class.
About the getter, I see no point of creating a custom method. I always use a getter with a proper access modifier.
I understand your point but isn’t it the reason why properties were introduced – to write validation inside their setters?
I get your point, but for me writing a complex setter is an anti-pattern. Same goes for using the ref, out keywords. Just because something was introduced, it doesn’t have to mean that it’s correct.
Ok, my curiosity is satisfied. Thank you very much! 🙂
Question out of topic. What OS are you currently using ? And why ?
This is Elementary OS, however, I tried also other distributions (really liked Antergos built on Arch Linux, but it doesn’t fully support .NET Core yet). I also have customized Ubuntu on my laptop, as the Debian distros are probably the most stable ones in terms of .NET Core support. I’ve been using a Linux for over half a year now and I really love it, the terminal is just great and I can natively use tools such as Docker, Nginx etc. Even more importantly, I have pretty much the same system, as the one that I’m using on the servers to host the .NET Core applications.
Can you provide english subtitle for videos?
Hey good tutorial,
What is the vscode theme you’re using ?