A few videos back I was telling you
about the Interface Segregation
Principle, which is the 'I' of the SOLID
code principles. And what we're saying
with that was that it's a good idea to
have small, stable interfaces. So have an
interface that just has a small number of
things. And we looked at some ways of
achieving that, but one of the ones I
didn't mention is to use a language
feature in C# which is a thing
known as extension methods, particularly
extension methods for interfaces. So what
I thought I'd do this week is just
quickly show you some examples of
extension methods in general, and then
show you how you can apply them to
interfaces and thus help with the
Interface Segregation Principle. So what
I'm going to show you here, if we look at
this program I've got this string simply
called 'LongIdentifierName' and the
problem I want to try to address here is
one that you encounter quite a lot when
you're doing particularly full stack
development, where you're using a mixture
of different languages, that different
languages have different naming
conventions for how identifiers, variable
names, method names should be put
together. So generally - certainly for public
methods - in C# we use what is known
as Pascal casing, which is what we can
see there, where each new word is given a
capital letter. We can't put spaces into
identifiers, and so we make it easier to
read by putting a capital letter to
identify each new word. Alternatives that
we have though: if we're writing in
JavaScript we tend to use a slightly
different thing called Camel casing -
Camel casing because the capital letters
provide these little humps in the middle
rather than having one at the beginning
as well, which is what we have in Pascal
casing. And then another type of casing we
have is what's used in HTML and CSS
which is what is known as Kebab casing
where everything is lowercase but the
gaps between words are indicated with a
dash. And so this is called Kebab casing
because it looks as though all the words
have been slid onto a kebab skewer.
So that's what try and do here, and what
we'd like to have is just an easy method
that if we take, say, Pascal casing in this case, we could
convert it to, let's say, Kebab casing and
we might want to do that a lot. And I can
tell you straight away that's something
that certainly .NET Core is
built into the system in many ways. So
this isn't probably something you have
to write yourself, but it's quite a nice
example of one of these extension
methods. So I've got this separate DLL
that I've called 'CaseConverters' and in
there I've got a class called 'Extensions'.
And when it comes to extension methods,
actually the name of the class itself - of
all places that we have classes in
C# - the name of the class here is
the least important. You can really call
it anything you like - we'll see why in a
bit - but calling it 'Extensions' is therefore
quite a common thing to do. So we make it
a 'static class' and what we can do in
there is we can put into that a 'public
static' method that's going to return a
'string' and we're just going to call this
'ToKebab'. So this is going to convert a
Pascal casing to Kebab casing, and we'll
put in the 'string' and we'll call that,
let's just say 'input', and actually, for
now I'm just going to do this without it
being an extension method, just write an ordinary method to do this. What we're going to need
is a 'StringBuilder' and then we'll
simply loop through each of the
characters and our 'input' and then we're
going to say 'if (c >=
'A' && c <= 'Z'', so if it's
uppercase, and if 'bob.Length > 0)' So if we're not at the
very beginning, because we don't want to
put a dash in at the beginning, then
we'll simply say 'bob.Append' and then
we'll put our '-' in. And then we'll do
a 'bob.Append' and we'll put our character in.
So every character goes in, but if it's an
uppercase character it's preceded by a
dash, unless it's right at the beginning
of the text. And then finally we'll do
a return, and so we do 'bob.ToString' and it should all be the
lowercase, so the easiest way to do that if
we just put our 'ToLower' at that point.
And then back in our Program we'll say
'string kebabbed ='
and then 'Extensions' gives us the
namespace there and then '.ToKebab'
of 'name'. And then if we print out the two
them side by side, we can see we've got
the Pascal version and the Kebab case
version. But at the moment that's not an
extension method, that is simply a static
method and the problem with it is it's
not necessarily obvious to someone using
a string that that is available to you,
because if I just type 'name.'
obviously we don't get that 'ToKebab'
there. We get a number of other
things; we don't get 'ToKebab' and so it's
not obvious that we've got it there. So
to make this into an extension method
all we have to do is, back on here we
simply add the keyword 'this'. Typical of
C#, reusing a keyword. You can kind
of see why they've called it 'this', but
it's not the same as 'this' in lots of
other situations that you'll be familiar
with. So now that we've done that, we can
actually here now we can simply say 'name.'
and we've got 'ToKebab' in it. And so
what's happened is, although strictly
speaking 'ToKebab' takes a parameter called 'input', we've actually moved that
parameter out of there and we've put it
before, so it looks as though 'ToKebab' is a
method of the 'string' class. But strictly
speaking it's not - it's an extension method.
But it just makes the syntax nicer. It
makes it easier to find in terms
of intellisense and it works in exactly the same way.
The thing we can see, though, is what I
mentioned: the name of the class is now
completely irrelevant, because that class
name 'Extensions', although obviously we've
got to have it there we declare the class, we never use it at
the point we're actually using the class.
So we just say 'name.ToKebab'. So we
need the name of the extension method. We do need to have the name of the
namespace, otherwise it won't know what
that is,
but the name of the class itself just
disappeared. We've just got to have a
class in which to store all of this. And
so what you'll find is that with an
extension method, whenever you declare it
with this first parameter marked with
'this' to be of the type you're extending.
So here we're extending string, but we
always call it with one fewer parameters
than we declare it. So for example, suppose
here actually we decided to make this a
bit smarter and we had 'char
replacement', and then rather than always
replacing it with the '-' we could replace
it with some different character. Then
here I would need to provide that, so if
I wanted to actually go for what's known
as snake casing, another type of casing
where the separator is the underscore
rather than the dash, then we could see
we could do it like that. But the thing
is it's always called with one fewer
parameters than its declared, because the
other parameter is provided on the left.
So that's just rather a trivial example
of how we could write an extension
method. If we were doing this 'ToKebab'
again and again and again and we wanted
an easy way of associating it with
string. Now let's look at how that fits
in with the idea of the Interface
Segregation Principle, and if you
remember what I had for that, I had this
program called 'TurtleDisplay' which had
a library - a couple libraries: TurtleLogic
and TurtleShapes. And if we run that up,
we had this idea that we could select
one of these. So if I select 'Line' there,
we can see we're using the turtle
graphics to draw a line. And the way that
worked was, if we look at this interface
ITurtle, when we were strictly obeying
the Interface Segregation Principle we
had got this ITurtle down to this
very simple interface: 'Up', 'Down' and 'Move'. So you can either have the pen up or
down. If it's down it's drawing, if it's
up it's not.
Then you can move to a location and with
those three methods you can actually
draw anything you like. Then what I've
got here is an example of us starting to
breach the Interface Segregation
Principle, because we've added another
method into the interface - in this case
'DrawLine'. And the problem was that
that's the thin end of the wedge; once
we've got a 'DrawLine' method, we're going
to end up adding a 'DrawCircle', a 'DrawSquare',  'DrawTriangle' and you've no
longer got the stable interface that you
wanted. And every time you change the
interface, even if your client code isn't
interested in drawing circles, they're
still going to have to build with the new interface. Now the way we solved it in the
previous video was to use actually the
Strategy pattern. So I introduced this idea
of an IShape interface, and we instantiated
that to draw circles and lines and so
forth. So that was one way of doing it,
but what I wanted to show you here was
another way of doing it that doesn't
require this separate interface  - can be
done entirely through the ITurtle
interface just using extension methods.
So if we go to this other library called
'TurtleShapes', you'll see in here I have
another class which, because I don't care
what the name is, I've simply called
'Extensions'. And what's going to be a bit
different here is what we're going to
extend. Rather than being a class we're
actually going to extend an interface.
And this is something that turns out to
be a very common use of extension
methods. And so let's steal a bit of code,
let's actually take that line code that
I actually had in the Turtle and I don't
really want anymore.
So if I just steal that line code, that
DrawLine
- let's also remove it from the interface,
so we're now back to a small stable
interface that just does what a turtle's
supposed to do. But let's put that into
our Extensions. And so we're going to have a 'DrawLine', but it's now going to be an
extension method that extends the ITurtle interface.
Okay. And what can we do with the ITurtle
interface? Well, we can do 'Up' we can do
'Down' we can do 'Move' and that's all we
need to have to draw a line. So all I
need to do here is just change that
slightly so it's 'turtle.Up', 'turtle.Move',
'turtle.Down', 'turtle.move'. And now back
in my TurtleDisplay, if I go to the
TurtleForm and go down to the draw
line, you can see here that was
previously calling the method in ITurtle, 'DrawLine' that I've just removed,
but now it's not working.
But it will work simply if we introduce
the appropriate namespace. So I'm already
referencing this TurtleShapes. If I just
get that namespace ... so now we've got the namespace in there and now we've got the
extension method and we're able to draw
the line. So when I run that up the draw
will work and so what I could go on and do is simply add further ones of these.
So they're calling DrawCircle, DrawSquare
and DrawTriangle on our turtle
interface. Now again we don't want to be
expanding that ITurtle interface to make
it bigger and less stable; we want to
introduce these as extension methods.
So I've actually done most of the work here
earlier. If I just go into this text file
and let's steal our DrawSquare our DrawCircle and just pop them in here.
The important thing about these is none of
them really does anything remarkably new.
All they're doing is calling the 'Up' the
'Down' and the 'Move' in various different
ways. So there's no need to add any
functionality to the turtle at all because
we're using the functionality it's already got. But we've added it
effectively to the interface by using
these extension methods. So now back in
the form we can see all of those things
work. The way we can tell they're
extensions is simply what we've got here
the little
down arrow in the intellisense - that's what
tells us we've got an extension.
But apart from that, just looking at the code
it's not really obvious at all.
But now when we run that up and now
we've got all of these things being
drawn, but they're being drawn through
the extensions, not through methods of
the interface itself. So it's just an
alternative way of solving the problem
we had before with the Interface
Segregation Principle. Once we've got our
narrow stable interface for ITurtle, we
don't want to be adding new methods.
Previously we saw an example using the
Strategy pattern which in certain cases
is going to be a perfectly good way to go.
Or here we've seen another approach by
using the extension methods. And bear in
mind these extension methods don't all
have to be in the same class or even in
the same library. So we could have
different providers just adding their
own extensions. So when somebody wants to
draw a pentagon, they could do it
entirely in their own DLL and as long as
we reference the DLL, have the namespace
then we can say 'turtle.DrawPentagon'
and it will just fit in seamlessly. And
the place in dotnet itself where this is
most widely used is on interfaces and
not just on interfaces, in fact on
generic interfaces, and the one that
we've been looking at quite a few times
in these videos, the IEnumerable. So if we go back to the
Program here and if I just put together
something like a 'List' - doesn't
matter what's in there, but if we look at
'ints.'
we can see it's got listed here in a
separate namespace, in 'System.Linq',
all of these very, very useful extension
methods that we've seen before,
things like Max and so forth. And we
talked about some issues with this in
the video on IEnumerable versus IQueryable; one of the things to remember here is
that extension methods are statically
bound. But even so - very, very useful. So if
I wanted to get the Max of all of those
'ints', I would simply have to do something
like this.
But the important thing is if we look at
'Max' then we can see it's the 'this' keyword
gives it away it's an extension method
for our  IEnumerable. And so that's
what gives our extension. So, we've seen
that we can use extension methods either
just on a single class like we did with
our 'ToKebab' on the string class. Or we
can write them as extensions to an
interface, which is a very useful way of
avoiding breaches of the Interface
Segregation Principle. Or we can just use
the ones already provided for interfaces
like IEnumerable. Hope you enjoyed that.
Do subscribe. To ask any questions - anything you'd like to see a video on, do ask it
in the comment section, and I'll see you
next time.
