Skip to content

Commit

Permalink
Merge pull request #60 from corndeladmin/oop
Browse files Browse the repository at this point in the history
adds oop videos
  • Loading branch information
nimonian authored Apr 11, 2024
2 parents 3537bca + 7332e76 commit 128371a
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 379 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ltex.language": "en-GB"
}
69 changes: 8 additions & 61 deletions src/js/classes-and-instances.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
# Classes and instances

<Vimeo id="933311165" />

In programming, a **class** is like a blueprint for creating objects. These
objects are known as **instances** of the class. Using a class for object
creation means objects are created the same way every time, which helps us
adhere to DRY (don't repeat yourself) principles.

## Defining a class

Let's imagine we're writing software to manage smart home devices. We're going
to be managing lots of smart lights, so we should make a class which will serve
as a blueprint for every smart light in the home.
We create a class using the `class` keyword.

```js
class SmartLight {
// class implementation goes here
}
```

As you can see, we use the `class` keyword followed by the desired name of the
class. In javascript, we capitalise the first letter of class names.
In javascript, it is conventional capitalise the first letter of class names.

## Creating instances

Expand Down Expand Up @@ -47,33 +46,13 @@ SmartLight {}
:::

The output shows us that we have two instances of `SmartLight`. But they don't
have any properties yet. They are empty objects. Let's change that!
have any properties yet. They are empty objects.

## Constructor function

A **constructor** is a special function inside a class that sets up new objects.
It's where we initialise properties.

```js
class SmartLight {
constructor(color, brightness) {
this.color = color
this.brightness = brightness
}
}
```

Inside the constructor, we can't refer to the instances as `kitchenLight` or
`bedroomLight` because they're still under construction! That's what the `this`
key word is for: it refers to the current instance under production inside the
class. Think of `this` as an unfinished smart light on the production line -
we're adding things to it as we go.

## Providing properties

We can pass values to the constructor when we create new instances. This is what
it looks like:

::: code-group

```js
Expand All @@ -98,44 +77,12 @@ SmartLight { color: 'cool blue', brightness: 50 }

:::

### Instances are objects

An instance of a class is just a javascript object, which means all of the
normal stuff you can do with objects still works.

::: code-group

```js
class SmartLight {
constructor(color, brightness) {
this.color = color
this.brightness = brightness
}
}

const kitchenLight = new SmartLight('warm white', 75)

// access properties with dot notation
console.log(kitchenLight.color)

// update properties
kitchenLight.brightness = 20
console.log(kitchenLight.brightness)
```

```console [output]
warm white
20
```

:::
Think of `this` as an unfinished smart light on the production line - we're
adding things to it as we go.

## Additional properties

There is no reason why all properties need to be passed to the constructor as
arguments. The constructor can add extra properties. This is useful for adding
default values that aren't intended to be provided by the creator of the
instance.
The constructor can add properties that are not passed in as arguments.

::: code-group

Expand Down
66 changes: 30 additions & 36 deletions src/js/getters-and-setters.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,27 @@ Let's revisit our smart light class:

```js
class SmartLight {
#color
#brightness

constructor(color, brightness) {
this.#color = color
this.color = color
this.#brightness = brightness
this.isOn = false
}

getBrightness() {
return `${this.#brightness}%`
if (this.isOn) {
return this.#brightness
} else {
return 0
}
}

setBrightness(level) {
if (level < 0 || level > 100) {
console.log('Brightness must be between 0 and 100')
} else {
this.#brightness = level
console.log(`Brightness set to ${this.#brightness}.`)
setBrightness(newBrightness) {
if (newBrightness < 0 || newBrightness > 100) {
throw new Error('Brightness must be between 0 and 100')
}

this.#brightness = newBrightness
}
}
```
Expand All @@ -41,27 +43,29 @@ Because the methods are concerned with getting and setting a property, they
could be refactored to make use of javascript's special `get` and `set` keywords
like this:

```js{10,14}
```js{9,17}
class SmartLight {
#color
#brightness
constructor(color, brightness) {
this.#color = color
this.color = color
this.#brightness = brightness
this.isOn = false
}
// [!code focus:13]
get brightness() {
return `${this.#brightness}%`
if (this.isOn) {
return this.#brightness
} else {
return 0
}
}
set brightness(level) {
if (level < 0 || level > 100) {
console.log('Brightness must be between 0 and 100')
} else {
this.#brightness = level
console.log(`Brightness set to ${this.#brightness}.`)
set brightness(newBrightness) {
if (newBrightness < 0 || newBrightness > 100) {
throw new Error('Brightness must be between 0 and 100')
}
this.#brightness = newBrightness
}
}
```
Expand All @@ -78,29 +82,19 @@ but crucially:

This is what it looks like:

::: code-group

```js
const bathroomLight = new SmartLight('amber', 50)

// this will fail, it calls "set .brightness(104)"
// this will throw an error, it calls "set brightness(104)"
bathroomLight.brightness = 104

// but this is ok, it calls "set .brightness(50)"
// but this is ok, it calls "set brightness(50)"
bathroomLight.brightness = 50

// this calls "get brightness()"
console.log(bathroomLight.brightness)
bathroomLight.brightness // 50
```

```console [output]
Brightness must be between 0 and 100
Brightness set to 50.
50%
```

:::

It is not necessary to use this special syntax, it comes down to style and
preference. So long as your class is well documented and other developers can
preference. So long as your class is well documented, and other developers can
use it, that is what's important.
97 changes: 33 additions & 64 deletions src/js/inheritance.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,63 @@
# Inheritance

Inheritance enables you to create a new class that is based on an existing
class. The new class inherits the properties and methods of the existing class.
This existing class is often referred to as the parent class, superclass, or
base class, while the new class is known as the child class, subclass, or
derived class.

## Understanding Inheritance

The primary benefit of inheritance is code reuse. You can define common
functionality in a parent class and then extend this class to create child
classes that inherit this functionality, adding or overriding properties and
methods as needed.

## Example Scenario

Let's continue with our smart home theme. Imagine we have a general
`SmartDevice` class that defines properties and methods common to all smart
devices, like connectivity status or device name. We can then create more
specific device classes (like `SmartLight` or `SmartCamera`) that inherit from
`SmartDevice` and add their unique features.
<Vimeo id="933310880" />

## Creating a parent class

First, we define our parent class, `SmartDevice`, with some basic properties and
methods.
If we want to create a set of classes that share some common functionality, we
should create a parent class. This parent class will contain the shared
functionality, and the child classes will inherit from it.

```js
class SmartDevice {
constructor(name) {
this.name = name
this.isConnected = false
constructor() {
this.isOn = false
}

connect() {
this.isConnected = true
console.log(`${this.name} is now connected.`)
togglePower() {
this.isOn = !this.isOn
}
}
```

We don't intend to use this class to directly create objects, but we'll use it
as a starting point to build our more specific classes.

## Inheriting from a parent class

Next, we create a child class that inherits from `SmartDevice`. We use the
`extends` keyword for inheritance in JavaScript.
To inherit from a parent class, we use the `extends` keyword, and then call the
`super()` method in the child class's constructor.

An important step is calling the special `super()` function inside the child's
`constructor()`. The `super()` function calls the parent's constructor, which is
necessary to set up shared properties like `name` and `isConnected`.
::: info

::: code-group
The `super()` method calls the parent class's constructor, passing in any
arguments that are needed for the parent class's constructor.

```js
class SmartLight extends SmartDevice {
constructor(name, color) {
super(name) // Calls parent's constructor with the name parameter
this.color = color
}
:::

::: code-group

changeColor(newColor) {
this.color = newColor
console.log(`${this.name}'s color changed to ${this.color}.`)
```js {1,3}
class SmartCamera extends SmartDevice {
constructor(location) {
super()
this.location = location
this.batteryLife = 100
}
}

const studyLight = new SmartLight('Epic Lamp', 'yellow')
console.log(studyLight)

// use the inherited .connect() method
studyLight.connect()

// use the child-specific .changeColor() method
studyLight.changeColor('ochre')
const poolCam = new SmartCamera('Pool House')
poolCam.togglePower()
console.log(poolCam)
```

```console [output]
SmartLight { name: 'Epic Lamp', isConnected: false, color: 'yellow' }
Epic Lamp is now connected.
Epic Lamp's color changed to ochre.
SmartCamera {
isOn: true,
batteryLife: 100,
location: 'Pool House'
}
```

:::

Because we have used `extends` and `super()`, we now have access to all
`SmartDevice` functionality on our `SmartLight`. Neat!

The next step would be to create a `SmartCamera` class which also inherits from
`SmartDevice`. Each child class doesn't need to implement things like `name` and
`connect()`, meaning we do not repeat and have to maintain all the repeated
code.
Notice that the `SmartCamera` class has access to the `togglePower()` method and
the `isOn` property, even though we didn't define them in the `SmartCamera`
class. This is because `SmartCamera` inherits from `SmartDevice`.
Loading

0 comments on commit 128371a

Please sign in to comment.