(upbeat music)
A few videos back I mentioned that the way
we were adding loot to the inventory wasn't a good idea.
And in this video we're going to look at why
that isn't a good idea and what we can do about it.
We're going to start though by deleting
the code we used for for loops
and un-commenting our player code.
So I'm just going to delete the for loop code
and I'm going to select or delete the rest of it first.
And select the rest of the code that was commented out.
You can come up to the code menu
and I can click on comment with live comment
which un-does the changes if it was already commented at.
And obviously these other things that were commented at,
were things that were commented at previously,
in case you're wondering why that's still
sitting there and commented at.
Okay, let's just run the programme
and make sure that it works.
Okay, we can see that we've got our code working again,
everything's working nicely.
So getting back to the problem at hand,
why isn't it a good idea for our main function
to add loot to Tim's inventory
in this fashion that it's doing here.
Well there's a couple reasons why it's not a good idea.
One reason is that main shouldn't have to know
that the players are using an array list to store the loot.
So looking at our player class
we've declared inventory to be an array list.
Now we may decide to change that in the future
to use some other kind of structure.
Now if we do that, we might break the code in main.
Our main function's expecting an array list
with an add method that it can use.
Now if we use something else, maybe a structure
that has a pin function instead of add
then the code in main would stop working.
Now one of the principles
of object oriented programming is encapsulation.
So encapsulation here, basically means
not exposing the inner workings of a class
to code outside of that class.
Now Kotlin takes a more relaxed attitude or approach
to encapsulating class properties.
Everything's public by default.
Now that's why the main function
can directly access inventory.
Now if we want to encapsulate our inventory,
we can mark it as private.
So come up here to the definition on line seven
and type private ahead of the val inventory
and now nothing outside of the player class
can access the inventory.
Now by default Kotlin makes properties and functions public,
which means that they're available to external code.
Anything that creates an instance of our player class,
for example, could access all it's properties and functions.
Now you can find the documentation and visibility
modifiers in Kotlin and I'm just gonna put the link,
bring the link up into a browser.
Have a quick look at that.
Now I'm not actually going to go through this document.
It can be quite confusing at the moment,
but it's worth bookmarking this page for reference later.
When you find some code that uses
the internal modifier, for example, you can
check back here to find out what that means.
So scrolling down quickly, we'll get
to the classes and interfaces section.
You see that it's a description
on the private modifier, up here.
Means visible inside the class only,
including all its members.
So by marking the inventory properly as private,
it's now no longer available to any code
outside of the player class.
Now of course that should mean
that we've now broken main.
So if I switch back to that now,
you can see we've got some errors showing.
So every time main tries to access a player's inventory
Android Studio is showing an error.
Now that's not good, but it's okay to get errors
while you're still developing your classes.
That's normal as you try things out and modify your design.
What's not good though, is modifying a class
once it's been used and breaking all the code that uses it.
So that's one of the things we're trying to avoid here.
Now if we don't want main or any other client code
of our player class to access the inventory directly,
then we need to provide a method for clients
to call when they need to add things to the inventory.
So I'm going to call that method or function
get loot and that reflects what's happening
when loot's added to a player's inventory.
So let's go ahead and do that.
Go back to the player class,
we're going to add that below, the toString function.
And again what we're doing is, we're creating
a function for clients to call when they need
to add things to the inventory.
We'll start with fun, space, getloot
(item
:loot
After {, it's going to be inventory.add item.
So now that we've done that, we can go back to our main
and we can change main to use the getloot function
instead of accessing the inventory directly.
So to do that we type, we select,
we type tim.getloot.
Those two changes there and then for the second one
we do something similar.
Getloot
The right number of parentheses
there to keep everything consistent and working okay.
We've now changed it so that the main function
is no longer accessing inventory directly.
Rather it's doing it via the getloot function.
Let's just run the programme
to make sure everything works as it did before.
And you can see we still got the same results when we run it
but now though main doesn't know
how the player class is storing the loot.
So it can call the getloot function,
passing on a piece of loot and leave details
of how to store it to the player class.
So our inventory is now encapsulated and isn't
available to any code outside of the player class.
Now another reason for encapsulating data like this,
is let's say we want to store the inventory
to disc whenever it changes.
That way we when I come to play the game again,
my inventory can be loaded back up,
and I'll still have all my loot.
Now before we encapsulated the inventory,
we'll have to add code to store it
each time we added new loot.
Now that's something we could forget to do,
which wouldn't make our players very happy.
So we could introduce a bug into our code quite easily.
But now though, we just need to add the code
to save the inventory in the getloot function.
In other words, to go back now to the player class and here,
code to save the inventory
goes here.
I'm not going to add the code to store the inventory
but you can see how we only have to put
this code in a single place now.
Whenever a new piece of loot gets added,
the inventory could be saved to disc,
and it will always be up to date,
and there's no chance of us forgetting to save it in main.
So that's another reason why
our previous approach wasn't a good idea.
Okay so if we're going to prevent external code
from accessing the inventory, we should
also provide a way to remove items from our loot.
So let's go ahead and write another function here.
We're going to call this one droploot.
It's slightly more complex than getloot,
because we need to make sure that we've got the item,
before trying to drop it.
So let's start with the definition,
fun droploot
(item: Loot
: Boolean.
Then we have that code block.
Then we're going to do return if
inventory dot
contains
item.
Close our parentheses and add another code block.
Inventory dot
remove
item.
True.
The next line, else,
false.
So I've used if as an expression there,
which we covered earlier in this tutorial section.
Now if the item of loot exists in the inventory,
we remove it and return true.
Now if it didn't exist it will return false.
So that's letting the calling code know
that if the item was successfully removed or not.
So let's see how that works in main,
we'll go back to main now.
And right down to the end here
after the last showInventory,
let's try and drop the red potion.
So we're going to put if
(tim
.droploot
that's going to be red potion,
Code block.
Tim
.showInventory.
So let's run the programme and we should find
that the red potion is removed from the inventory.
So you can see we've got our first printout
of Tim's inventory, the red potion was there.
Then after the call to the droploot,
the second time, the second block of code here,
the second block of output,
the red potion is no longer there.
Now we should also test what happens
if an item can't be removed.
So I'm going to create a new piece of loot
and try to remove it from Tim's inventory.
Let's go ahead and do that.
And let's also change this to put an else in here.
Else, code block.
And it'll be
println,
you don't have a
$, that's a right curving braces
redpotion.name
So we're printing something out that's not there,
but let's go ahead and create this other item as I mentioned
val blue potion
is equal to loot
("blue potion"
loot type.potion
and let's we'll set the value six
Then we'll try doing a drop,
even though we haven't gotten it in the inventory.
So if tim.droploot
bluepotion
tim.showInventory
Else
Code block again
That's going to be println
You don't have
Pound sign
That's right curving braces, bluepotion.name.
Alright so let's try running that.
What I should probably do to avoid some confusion
is just delete that last line there.
So let's just try running it again.
And you can see this time we got the message,
you don't have blue potion.
So that's working well.
Our players can now pick up loot and drop it again.
But what about something
like the Invisibility Potion though?
You can see that the way we've defined it,
we haven't actually got a reference to it in main.
So we can't call the droploot function to drop it.
So that's probably something we probably shouldn't
have called getloot the way we did
for the Ring of Protection and Invisibility Potion
on lines 41 and 42 here.
So this is what I meant earlier,
when I said we need to consider who should own the objects.
Now both those objects really belong to the game.
We should really have created variables to hold
a reference to them in main, so that's how
we've got a way to drop them.
The way we've got our code currently,
there's no way to reference,
there's no reference to either of those bits of loot,
so we've got no way to get Tim to drop them.
Now that's the sort of thing you need
to be aware of when designing your programmes.
After coding for a while, you'll make
the correct decision the first time, most of the time.
Now you still make mistakes occasionally, of course.
Just like I did there, with those two items.
That's fine, that's all part of the learning process.
But of course, in a real game, we'd probably
give the players a way to go through their inventory
and choose items to drop, so in that case
we wouldn't have this problem.
So in our code main is in charge of controlling everything.
The way to fix this then is to change main,
so that we've got references to the Ring and the Potion.
But I'm not going to do that
because this gives us a good excuse
to look overloading a function
and we'll do that in the next video.