Video Course Preview: API in C#: The Best Practices of Design and Implementation


Hi, welcome to the ¬タワAPI in C#: The Best
Practices of Design and Implementation¬タン
course, where you will learn how to design and
implement robust and maintainable APIs
relying on the best practices which are tried-and-tested by the .NET
community all over the world. My name is Elias Fofanov, aka EngineerSpock
and I¬タルll be leading you through the course. I began my career as a postgraduate student
participating in Microsoft Imagine Cup contest. I’ve been working with the .NET platform since
2003. I’ve been professionally architecting and
implementing software for nearly 7 years,
primarily based on the .NET platform. I’m passionate about building rich and powerful
applications using modern technologies. I’m a certified specialist in Windows
Applications and Service Communication
Applications by Microsoft. API stands for Application Programming
Interface. APIs are everywhere. Roughly
speaking, all the functions you ever call are the That¬タルs why it is so important to understand
how to build convenient, robust and easily
maintainable APIs. By the end of this course, you¬タルll get a deep
understanding of what is a good API. You¬タルll become enough educated to reason
about pros and cons of particular API design
and implementation decisions. This understanding will be based on the
understanding of the fundamental principles which underlie the process of designing and
implementing APIs. A side benefit of taking this course is that many
topics are independent of each other what allows me to easily extend this course by
new topics from time to time. I¬タルll announce
all the updates, so you¬タルll never miss In this course, we will look atThe
characteristics of a well-designed API and the
API development principles How to give better names for API members
and what naming conventions exist in the .NET
platform and suited for C#. Common problems encountered by C#
developers in the process of designing and
implementing APIs: classes vs structures, abstract classes vs
interfaces, creational patterns vs constructors, how to implement the ¬タワdispose¬タン pattern
(are you sure you understand this allegedly
simple case?) Common implementation smells such as poor
naming, excessively long methods, output
parameters and so on. Common Architectural Design Smells such as
Primitive Obsession, Hidden Dependencies,
Violation of Law of Demeter and other. How to deal with errors. It is surprisingly hard to
develop robust software where errors handling
is based on exceptions. We will find out why this is so and how to
struggle with problems of error handling. How to deal with Nulls. Null Values have
always been a real pain in the ass. The NullReferenceException is a well-known
and popular guest in our software. We will look at the possible ways of diminishing
the disrupting power of null values. The goal of this course is to give you a solid
understanding of the fundamental principles of
building great APIs. Another goal is to teach you to build great APIs
specifically in the C# language since there are
many cases specific for the C# language. This course is aimed at all the C# developers,
from beginners to seniors. Topics which are covered in the course are
relevant for all kinds of C# developers since all
developers design and implement APIs. The topics complexity is very different. There
are plenty of very simple topics, and at the same time, there are topics which
require from you a solid C# background. You¬タルre not forced to watch all the course
sequentially. On the contrary, if you are an expert, feel free
to skip the topics you understand very well.Feel
free to look at the course description. I’m looking forward to seeing you inside the
course. Enroll and start learning the ¬タワAPI in C#: The
Best Practices of Design and Implementation¬
タン course! Hello, my name is Elias Fofanov and welcome
to the course. This course will show you how to
design and implement types using best You will learn how to write nice and clean code,
so the other developers will appreciate you, not
hate you. It is very common to see unreadable, dirty,
smelly code even if the code has been written
in C#, despite the power of this language. To write code of high quality, it is absolutely
insufficient to learn how to use the features of
C# such as lambdas, generics, delegates, extension methods and many other. If you know how to use all these features, it
does not mean you know how to use them
properly. What does it mean? Well, the point is that most code sucks and this
is the utter truth. You definitely don¬タルt want
to write code that sucks, don¬タルt you? If you want to write code professionally, then
you have to understand the characteristics by
which your code might be deemed as horrible or excellent. Moreover, you have to grasp many other topics
except how to use a wide range of the C#
features, in order to produce code of high So, this course is about producing high-quality
code by making it simpler, logical and
extensible in the context of APIs development. You will learn all these things in examples from
the real world. I will be your personal instructor and I will lead
you through the whole stack of problems,
which you will face in the real world. This course covers the vast majority of topics: How to give better names for API members
and what naming conventions exist in the .NET
platform and suited for C#. Common problems encountered by C#
developers in the process of designing and
implementing APIs: classes vs structures, abstract classes vs
interfaces, creational patterns vs constructors, how to implement ¬タワdispose¬タン pattern (are
you sure you understand this allegedly simple
case?) Common implementation smells such as poor
naming, excessively long methods, output
parameters and so on. Common Architectural Design Smells such as
Primitive Obsession, Hidden Dependencies,
Violation of Law of Demeter and other. How to deal with errors. It is surprisingly hard to
develop robust software where errors handling
is based on exceptions. We will find out why this is so and how to
struggle with problems of error handling. How to deal with Nulls. Null Vales have always
been a pain the ass. NullReferenceException is
a well-known and popular guest in our software. We will look at the possible ways of diminishing
the disrupting power of null values. In this introductory module, we will talk about API characteristics, public and private APIs and the development principles of APIs Fine! Let¬タルs move to the next lecture where
we will talk about API characteristics. One of the most popular questions on
interviews for C# developers is ¬タワwhat is the
difference between class and structure?¬タン. Most interviewers expect that you¬タルll answer
that classes are reference types allocated on
the heap, whereas structures are value types allocated
on the stack. Most of the candidates answer
exactly that. While this is mostly correct, the deeper answer
is different. The main difference between structures and
classes is that structures implement the
semantic of copying by value, whereas classes implement the semantic of
copying by reference. This is the most important aspect, especially
from the API designing perspective. The only case when allocation method matters
is the case of performance problems. If profiling data shows you real problems, then
you might be interested in converting reference
types into value types. Let¬タルs consider a good and bad examples of
using structures. Let¬タルs switch to a visual studio and look at
an example. Here is a structure named Mutable which
contains an integer value. It also has a public method which increments
that integer value by one and returns the result.
This is a very bad idea and you¬タルll see why Consider also the following client code. We
have a class named A here which contains
three members of type Mutable. The first one is a property with getter, the
second one is a public readonly field, and the
third one is a regular field without any In the main method we make three call pairs to
the IncrementX method for each case. What do you think will be printed out to the
console? Both in the first and second cases 1,1 will be
printed out, while in the third case it will be 1,2. Let¬タルs explain why this is so. In the first case we work with a property which
has a semantic of returning a value, it leads to
copying a new value on each call, so each call to PropertyMutable returns a brand
new copy of the Mutable structure. The second case is the interesting one. Seems
like we have a direct access to a variable here, but the thing is that compiler generates
temporal variable for calls to readonly fields, so
in the reality it will look like in the following In the third case we have a direct access to the
structure instance. So, in both calls we work
with the same instance. Do you feel how bad the design of that
structure is? It¬タルs a pure evil. This is because that structure which
implements the copy by value semantic by the
language design is implemented as a mutable reference type in
the code. Let¬タルs look at a good example of a structure. Here you can see the Point structure which
contains two values of type int. It exposes the Increment operation which
always return a new instance of a structure. So, we don¬タルt modify the internal state of the
instance, we just always create a new one.
This is true immutability. It¬タルs impossible to misuse a type which is
designed in this way. Mutability logically implies referential identity;
values do not have a referential identity, that¬タルs why they¬タルre called value types,
not reference types. If you need to mutate something, make it a
reference type. Output parameters are often used when we
want to return more than one parameter from a
method. Generally speaking, this is a code smell and a
bad idea except for the TryParse pattern
because it is widely used in the BCL and it became sort
of a convention between C# developers. I¬タルve seen a great number of times code like
in the following example: God¬タルs sake. This is clearly abusing of out
parameters. How the hell the client¬タルs code
is supposed to look like? I¬タルm not even going to show you what it will
look like. That code will just look insane. In such situation, you have to refactor out all
the arguments into a separate class. In this case, it might be a class named
RailwayDictionariesContainer which contains
all the required information. As a result, the GetDictionaries method would
have been looked like in the following example: The second problem here is that the initial
developer of this method wanted to return an error code in the case of errors during
retrieving all the information. In the second version, we still return that error
code as a result of the function call. How can
we make this method better? In order to overcome this problem, you can rely
on a Result monad which will be discussed in
details in the module about errors handling. In short, the Result monad is a class written in
a functional style, so it allows you to store an error, a returning
value and allows you to build method chains. Look at the module ¬タワDealing with Errors¬
タン, lectures ¬タワErrors and Functional
Programming¬タン and ¬タワPipelining by Here I just will show how the signature would
have been looked like: Do you feel how much simpler is this
comparing to the first version? Let¬タルs
consider another refactoring. Sometimes we just want to return a couple of
values from a function and because developers
are very lazy they don¬タルt want to create a separate
structure or a class for them. Most likely, C# 7
will get an improved support of so-called tuples. So, we will be able to write a function with the
following signature, for example: Surprisingly, the caller will be able to use this
function as in the following code snippet: As you can see, you get the ability to use
named returned arguments as they were
named in a function declaration. This is So, I hope we will get this feature in C#7. This
is also a great way for avoiding out parameters. Thus, strive to use out parameters as rare as
possible. The most common way of using out-
parameters is to use them in the TryParse
pattern. Ok, we¬タルre done with ¬タワout¬タン Let¬タルs talk a little bit about comments. If we look at Wikipedia, we will see that ¬タワthe
Law of Demeter (LoD in short) or principle of
least knowledge is a design guideline for developing software,
particularly object-oriented programs. In its general form, the LoD is a specific case of
loose coupling. Ok. sounds great, but what
does it really mean? A general formulation of the law is: Each unit
should have only limited knowledge about
other units: only units ¬タワclosely¬タン related to the current
unit. Or: Each unit should only talk to its
friends; Don¬タルt talk to strangers. Great! Sounds clearer, but still not enough
practical. The formal object form of the law can be
summarized as: A method of an object may only call methods of: 1. The object itself. 2. An argument of the method.3. Any
object created within the method. 4. Any direct properties/fields of the object. Oh, at last, the practical meaning becomes
clear. Moreover, all these guidelines, in the
end, came to the following simple statement: ¬タワdon¬タルt talk to strangers¬タン. Another
simple formula of this law is ¬タワuse only one
dot¬タン. Ok then. Let¬タルs consider a practical example which
was suggested long ago by David Bock as a
great illustration of the problem. Consider the following case. Let¬タルs pretend that we should model the
business relationships between a paperboy
and a customer who wants to buy magazines. A paperboy rings the doorbell, a customer
opens it, a paperboy somehow has to be paid
and then hand over a magazine to the Let¬タルs look at the code example which
models the case. Here is a simple Customer class which has first
and last names and it also has a Wallet of type
Wallet. Here you can see the Wallet class. It is
extremely simple; it just stores the decimal
value which determines the amount of money The client of the Wallet API can add money to
the wallet, can withdraw money and check the
current amount of money. Looks good and simple. It is just fine and
should fit our needs. The last thing is to
implement the logic of a paper boy. Let¬タルs The PaperBoy class exposes a single method:
DeliverMagazine which takes the cost of that
magazine and the customer to which a paper boy has to
deliver the magazine.Looks great! Let¬タルs
look at the implementation. Paperboy just takes the wallet of a customer
and then checks the amount of money. If there is enough money, the paper boy just
takes the needed sum, otherwise, a paper boy
just goes away. Great, one would say. What do you think? Is it
really great? If you think a little bit deeper, then you may
come out with the idea that there is something
strange in the fact that a paper boy takes the wallet of a
customer in order to be paid. A paper boy has
access to customer¬タルs wallet, what a Would you be so kind to give your wallet to
unknown paper boys? I doubt. So, in this case, the violation of the Law of
Demeter is obvious. The PaperBoy operates
with a wallet through a customer. The first dot here in the code is when a
paperboy takes the wallet. Then there is a second dot when he checks the
amount of money in the wallet. The same case
when a paper boy withdraws some money. To describe the problem more seriously I
should say that the main problem with this
design is that ¬タワthe paperboy is being exposed to more
information than he needs to be¬タン. All the
problems are the consequences of this fact. So now these three classes are tightly coupled.
If we change the Wallet class, we may have to
make changes to both of the other classes. By the way, if a Wallet is null, then a PaperBoy
will throw the NullReferenceException, so it might be that in the real world code base,
the PaperBoy will have to deal with it
introducing null checks and as a consequence cluttering the code.So,
how to fix the problem? In order fix the problem we can just model the
real world scenario. When a paper boy delivers
a magazine he just asks for a payment. Let¬タルs look at the improved design. Now, the Customer class exposes a public
method named GetPayment, while the
underlying Wallet object is now contained in a No one, except the customer, has access to its
Wallet. The paper boy now is forced to ask the
customer to get a payment. Paperboy now does not have the rights and by
the way the responsibility as in the previous
design to fumble in the customer¬タルs wallet. So, the improved design is better in three
ways:1. it better models the real world
scenario¬タᆭ The Paperboy code is now ¬タリasking¬タル the
customer for a payment. The paperboy does
not have direct access to the wallet. 2. the Wallet class can now change, and the
paperboy is completely isolated from that
change¬タᆭ 3. we are now free to change the
implementation of the ¬タリGetPayment()¬タル
method. To sum up, I want to say, that the rule about a
number of dots sometimes can be deceitful. The thing is that the Law of Demeter is not
about the number of dots. This law is about
reducing the coupling and improving the When we are talking about data structures like
data structures which are exposed by the Excel
document it¬タルs perfectly fine when its API allows you dig the structure of the
document likeExcelDocument.Sheet.Cell and
so on. The same can be applied to Fluent APIs, when
each call returns an object, so you can build
method chains. You¬タルll see the method chaining approach in
the module about errors handling. So, as always, don¬タルt treat rules as dogmas
and try to understand the underlying meaning
of the rule, instead of applying rules blindly. At first, we need to understand what types of
errors exist. Generally, there are four types of
errors: ¬タᄁ Recoverable and unrecoverable
system errors ¬タᄁ Recoverable and unrecoverable user
code errorsLet¬タルs consider them in turn. Since we use BCL extensively, we can¬タルt
evade exceptions anyway. For example, working with the file system or
network, you¬タルll get corresponding
exceptions raised by BCL logic: FileNotFoundException, SocketException, and
many others. These exceptions are
recoverable system errors and they should be It would be ridiculous if you let these
exceptions to kill your application. In such a
case, our application will be crashing very often. There some kind of system errors which can¬
タルt be correctly handled. StackOverflowException and
OutOfMemoryException belong to the class of
unrecoverable system errors. Even if you want to catch them you can¬タルt
do that, CLR will just terminate your application. You can¬タルt eliminate exceptions we
discussed so far. All you can do is to handle
recoverable exceptions. Unrecoverable user code errors are the bugs in
your code, mostly. For example, invalid input into a function is a
bug in the code and fairly an application should
fail in such a case. NullReferenceException also signalizes a bug
in your code and frankly speaking, an
application also should fail in this case. We will discuss the ¬タワcatch them all
approach¬タン a little bit later. The next class of errors is ¬タワrecoverable
user code errors¬タン. This type or errors represent those errors which
we expect to happen, for instance when
validation of business rules fails. C# books don¬タルt make any difference
between different types of errors, so they suggest to rely on exceptions in the
case of recoverable user code errors as well as
in any other cases. And this is horrible. Even error codes are better than exceptions
when dealing with this types of errors. Though, we will not use error codes. There is a
much more elegant way which we will learn a
little bit later. Remember, errors are not exceptions. An
exception is a form of an error representation. The original meaning of throwing exceptions is
to fail fast, unwinding the stack, until the
appropriate catch block will handle the The most important part here is ¬タワunwinding
the stack¬タン, this is the main difference from
returning an error. Many developers and the entire community
enforces the idea that exceptions are good and
error codes are bad. Also, you can hear that the following code is
pure evil: Well, indeed, I agree that this exception
handler looks pretty bad. But is it helpful to
know only that? No, it is not. Let¬タルs consider the problems
which accompany using of exceptions in the
next lecture. The most common problem with nulls is that
nulls make code harder to read, we tend to
use defensive coding practices, we pollute the code base by all those null-
checks here and there, gosh they are
everywhere. Another problem which is connected with the
first one or maybe a corollary of it, is that in the
end, it’s very hard to control nulls everywhere so
sooner or later we end up with getting the
NullReferenceException. NullReferenceException is a real pain in the
ass on every project. Another problem with nulls is semantical.
Inappropriate using of nulls even makes
application¬タルs design worse. Once, I was reading some code in a
ViewModel and stumbled upon a method
implemented as it follows: This method has several drawbacks from the
reading perspective. But right now I want to
stress out only one. The method¬タルs meaning payload is buried
inside a bad method name and the uncertainty
of the following check: Look at the square violet square. The overall meaning is that we have here
some sort of checking the smart card. When readers look at this method, specifically
at the line of the first null-checking, they will ask themselves about why we have
one card description passed as an argument
and after that we¬タルre trying to search The answer to this question, as it turned out,
was that this method is performing a double-
check. The first argument is the description of a smart
card which has been already read in the
previous step and we are trying here to check that at the
current step we are handling the same smart
card (that it wasn¬タルt removed forcibly from the
reading field, for example). The main problem here is that there is no
strong semantic cohesion between null-
checking in the if-statement and the logic inside When I faced that code I questioned myself
about what did actually mean the fact that the
object is actually null or not. What I wanted to say is that Null-checks could
be completely uncertain and vague by the
nature of the NULL as a state of an instance. NULL is nothing, but what does ¬タワnothing¬
タン actually means? No one knows. There are several practices for fighting with
nulls. One of them is the time-honored Null
Object pattern. This is one of the easiest ways to represent
nulls. The Null Object pattern is about creating a
special case for each type in order to represent
its null state. Here you can see the diagram which
represents the design of this pattern. We can imagine almost any class as real entity
which potentially can be null. Our goal is to eliminate nulls when working with
this reference type. In order to do that we need to extract a base
class which is represented on this diagram as
an Abstract Entity. Please notice that we extracted the API of the
real entity which is represented by a method
named ¬タワDoSomething¬タン. After that we can roll out a NullEntity which
inherits from that base class. The implementation of the doSomething
method will be empty in this case. So, when a method needs a real entity it
should require an abstract entity to be passed
in as an argument. If other methods which return abstract entity
need to return a null value, they will return a null entity, so, if throughout
the system we use Null Entities instead of
nulls, then we will not get null reference Let¬タルs consider an example.Let¬タルs
pretend that we build some kind of a caching
system. Here, we define the ICacheStorage interface
with three members: Remove, Store, and
Retrieve. One of the useful implementations would be
the HttpContextCacheStorage which caches
data in the HttpContext. And here is the NullObjectCache which
implements the same caching interface. What
this implementation does? It literally does nothing.Look at the possible
client¬タルs code. What might have happened if the argument of
the PerformWork method would have been
null? NullReferenceException would have been
thrown from the attempt to retrieve an object. Now consider what will happen if we pass an
instance of the NullObjectCache class.
Nothing. Literally nothing. Sometimes this is a really desired behavior and
in such cases, this pattern provides a safety
net. So, remember, this pattern is useful when you
know that there is a meaningful default
behavior. ¬タワdoing nothing¬タン is a meaningful
behavior in certain cases. If this is inappropriate for some reasons, then it
might be better to stick with nulls or implement
other strategies of avoiding nulls.

Leave a Reply

Your email address will not be published. Required fields are marked *