Professional Documents
Culture Documents
#Programming:: The Basics
#Programming:: The Basics
#include <stdio.h>
void greet() {
printf("Hello, World!");
}
int main() {
greet(); // This is a procedure call
return 0;
}
loves(romeo, juliet).
Each paradigm has its strengths and weaknesses, and the choice of
paradigm can be influenced by other factors such as the specific problem
the program is trying to solve and the programming language in use.
Procedural programming can be useful for simple, straightforward tasks,
while object-oriented programming is widely used in large software
systems. Functional programming is becoming increasingly popular for
tasks that require concurrency or for programs with no side effects.
Logic programming is useful for tasks that involve complex rule systems
or inferences.
1. Start
2. Read the first number (num1)
3. Read the second number (num2)
4. Add num1 and num2 and store the result in sum
5. Display sum
6. Stop
START -> Input num1 -> Input num2 -> Add num1 and num2 -> Store the result in
sum -> Display sum -> END
Variables and constants have a name and a data type associated with
them. The name allows us to refer to the variable, while the data type
defines the kind of data that the variable can hold.
Sometimes, you may need to convert a value from one data type to
another. This is known as type conversion or casting. The two main
types of casting are implicit casting (also known as coercion) and
explicit casting.
int num1 = 5;
float num2 = (float) num1; // Explicitly cast num1 to a float
if condition:
# Code to be executed if condition is true
if condition:
# Code to be executed if condition is true
else:
# Code to be executed if condition is false
if condition1:
# Code to be executed if condition1 is true
if condition2:
# Code to be executed if both condition1 and condition2 are true
else:
# Code to be executed if condition1 is true and condition2 is false
else:
# Code to be executed if condition1 is false
do:
# Code to be executed
while condition
Control flow statements allow you to control the flow of your program.
They include:
Functions are blocks of reusable code that perform specific tasks. They
allow you to break down your program into smaller, more manageable
parts, making your code modular and easier to understand. Functions can
take inputs (arguments) and produce outputs (return values).
def function_name(parameters):
# Function code
return value # (optional)
Arrays and lists are data structures that allow you to store multiple
values of the same or different data types. They provide a convenient
way to work with collections of data.
Arrays and lists have a wide range of algorithms and operations that
can be performed on them, such as sorting, searching, and traversing.
Some common algorithms include:
print("Hello, World!")
In addition to console input and output, programs can read from and
write to files. File input and output operations are essential for data
storage and retrieval.
try:
# Code that might raise an exception
except ExceptionType:
# Code to handle the exception
The catch block specifies the type of exception to catch. You can have
multiple except blocks to handle different types of exceptions or
provide a generic except block to handle any exception.
try:
# Code that might raise an exception
except ExceptionType:
# Code to handle the exception
finally:
# Code to be executed regardless of an exception
Error handling and exception handling are essential for building robust
and resilient programs that can gracefully handle unexpected
situations.
Example (Python):
def sanitize(input):
return ''.join(e for e in input if e.isalnum())
Example (Python):
try:
risky_operation()
except Exception:
log_error("An error occurred.")
show_user_friendly_error_message()
Some of the most widely accepted and effective secure coding practices
include:
class Circle:
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
return 3.14 * self.radius * self.radius
class Animal:
def __init__(self, name):
self.name = name
def sound(self):
pass # Placeholder method
class Dog(Animal):
def sound(self):
return "Woof!"
class Cat(Animal):
def sound(self):
return "Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Person:
def __init__(self, name):
self.name = name
def __del__(self):
print("Destructor called")
person = Person("John")
del person # Destructor called
import re
import re
Example (Python):
import threading
def print_numbers():
for i in range(10):
print(i)
def print_letters():
for letter in 'abcdefghij':
print(letter)
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
thread1.start()
thread2.start()
In this example, two threads are created: one to print numbers from 0
to 9, and the other to print letters from 'a' to 'j'. The threads are
started with start(), and join() is used to make sure the program waits
for both threads to finish before it terminates.
Example (Python):
import threading
class BankAccount:
def __init__(self):
self.balance = 100 # shared resource
self.lock = threading.Lock()
account = BankAccount()
thread1 = threading.Thread(target=account.update, args=[50])
thread2 = threading.Thread(target=account.update, args=[-75])
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(account.balance) # prints 75
Deadlocks and race conditions are two of the main issues in concurrent
programming.
● TCP takes care of the data delivery, but it doesn't worry about
the packet structure or the control of the network.
● IP takes care of the routing of packets. It is responsible for
getting each packet of data to the right destination.
This SQL query selects all fields from the "Employees" table where the
"Salary" field is greater than 50000.
NoSQL databases are a type of database that can handle and sort all
kinds of data, everything from unstructured text documents to
structured data. NoSQL databases became popular as web applications
become more complex and required more diverse ways to store and query
data.
1. Singleton Pattern: Ensures that a class only has one instance, and
provide a global point of access to it.
2. Factory Method Pattern: Defines an interface for creating an
object, but lets subclasses decide which class to instantiate.
3. Abstract Factory Pattern: Provides an interface for creating
families of related or dependent objects without specifying their
concrete classes.
HTML stands for Hyper Text Markup Language. It is used to describe the
structure of web pages using markup. HTML elements are the building
blocks of HTML pages and are represented by tags.
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
CSS stands for Cascading Style Sheets. It is used to control the style
and layout of multiple web pages all at once.
body {
background-color: lightblue;
}
h1 {
color: navy;
margin-left: 20px;
}
Frontend and backend development are two sides of the same coin. The
frontend of a web application is the part that users interact with.
It's built with languages like HTML, CSS, and JavaScript.
There are many scripting languages, each with its own strengths and
purposes. Here are a few examples:
● Bash: A Unix shell and command language, good for file manipulation
and program execution.
● Python: A high-level, interpreted language, Python is often used
for web and Internet development, scientific applications, data
analysis, and more.
● JavaScript: Primarily used in web development to add interactivity
to web pages.
● Ruby: A dynamic, open-source programming language with a focus on
simplicity and productivity. It has an elegant syntax that is easy
to read and write.
● Perl: A highly capable, feature-rich programming language with
over 30 years of development.
import os
def rename_files():
file_list = os.listdir(r"<path to directory>")
os.chdir(r"<path to directory>")
for file_name in file_list:
os.rename(file_name, file_name.lower())
rename_files()
print(variable_name)
import pdb
pdb.set_trace() # Set a breakpoint
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("Debug message")
logging.error("Error message")
import unittest
class TestAddNumbers(unittest.TestCase):
def test_add_numbers(self):
result = add_numbers(2, 3)
self.assertEqual(result, 5)
if __name__ == '__main__':
unittest.main()
import unittest
class TestMultiply(unittest.TestCase):
def test_multiply(self):
result = multiply(2, 3)
self.assertEqual(result, 6)
if __name__ == '__main__':
unittest.main()
Version Control Systems (VCS) are tools that help manage changes to
source code and facilitate collaboration among developers. VCS allows
you to track modifications, revert to previous versions, and merge
changes made by multiple developers.
Version control systems manage code repositories, which store the code
and its history. Repository management involves tasks such as creating,
cloning, and managing branches.
git merge <branch_name> # Merge changes from branch_name into the current
branch
# Git will mark conflicts in the file. Edit the file to resolve conflicts and
choose the desired changes.
git add <file_name> # Stage the resolved file
git commit -m "Merge conflict resolution" # Commit the merged changes
There are different methodologies that can be followed within the SDLC.
Two popular approaches are Agile and Waterfall:
Project management tools help manage and track the progress of software
development projects. They provide features for task tracking, team
collaboration, and overall project management. Some popular project
management tools include:
let x = 5; // x is 5
const pi = 3.14; // pi is a constant 3.14
4- Objects: Objects are variables too. But objects can contain many
values.
let x = 5;
let y = x + 2; // y is now 7
if (x > y) {
// do something
} else {
// do something else
}
function myFunction(x, y) {
return x * y;
}
9- Strings and String Methods: Strings are useful for holding data that
can be represented in text form. There are many methods that can be
used with strings including length, indexOf, search, replace, etc.
10- Number and Number Methods: JavaScript has only one type of number.
Numbers can be written with, or without decimals. JavaScript numbers
can also include + and - , and e to indicate exponents.
console.log(Math.PI); // 3.141592653589793
console.log(Math.sqrt(16)); // 4
try {
notAFunction();
} catch(err) {
console.log(err); // ReferenceError: notAFunction is not defined
} finally {
console.log('This will run regardless of the try/catch result');
}
17- AJAX: AJAX is about updating parts of a web page, without reloading
the whole page. It stands for Asynchronous JavaScript and XML.
function makeAdder(x) {
return function(y) {
return x + y;
};
}
let add5 = makeAdder(5);
let add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
21- Arrow Functions: Arrow functions allow for a shorter syntax when
writing functions. Arrow functions do not have their own this.
23- Spread Operator and Rest Parameters: The spread operator allows an
iterable to be expanded in places where zero or more arguments are
expected. The rest parameter syntax allows a function to accept an
indefinite number of arguments as an array.
// Spread operator
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5, 6]; // [1, 2, 3, 4, 5, 6]
// Rest parameters
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
25- Modules: JavaScript modules are a way to share and reuse code
across files.
// lib/math.js
export function sum(x, y) {
return x + y;
}
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
28- Iterators and Generators: Iterators are objects that know how to
access items from a collection one at a time, while keeping track of
its current position within that sequence. Generators are a special
class of functions that simplify the task of writing iterators.
function* idMaker(){
let id = 0;
while(true) {
yield id++;
}
}
console.log(iterator.next().value); // Output: 1
console.log(iterator.next().value); // Output: 2
29- Map, Filter, and Reduce: map, filter, and reduce are all array
methods in JavaScript that provide a functional programming style.
31- NaN: NaN is a special value that stands for "Not a Number". It is
used to indicate an undefined or unrepresentable value.
console.log(Math.sqrt(-1)); // NaN
32- Null and Undefined: Both null and undefined are special values in
JavaScript. undefined means a variable has been declared but has not
yet been assigned a value. null is an assignment value. It can be
assigned to a variable to represent no value or no object.
let test;
console.log(test); // undefined
test = null;
console.log(test); // null
33- Truthy and Falsy: Every value in JavaScript has an inherent boolean
value. When that value is evaluated in the context of a boolean
expression, we say that value is either truthy or falsy.
let x = "5";
console.log(x + 1); // "51"
console.log(+x + 1); // 6
const obj = { a: 1 };
obj.b = 2;
console.log(obj); // { a: 1, b: 2 }
function greeting(name) {
console.log('Hello ' + name);
}
function processUserInput(callback) {
let name = prompt('Please enter your name.');
callback(name);
}
processUserInput(greeting);
let animal = {
eats: true
};
let rabbit = Object.create(animal);
console.log(rabbit.eats); // true
40- Web APIs: Web APIs provide the functionality to create a dynamic,
interactive web application. These APIs include DOM manipulation, Fetch
API, Geolocation API, Web Storage, and more.
fetch('https://api.github.com/users/github')
.then(response => response.json())
.then(data => console.log(data));
41- this Keyword: this keyword refers to the object that is executing
the current function.
const person = {
name: 'John',
greet: function() { console.log('Hello, ' + this.name); }
};
person.greet(); // 'Hello, John'
setTimeout(() => {
console.log('Runs after 2 seconds');
}, 2000);
setInterval(() => {
console.log('Runs every second');
}, 1000);
44- Local Storage: Local Storage allows you to access a local Storage
object. Data stored persistently and isn't sent with every server
request.
localStorage.setItem('myKey', 'myValue');
let data = localStorage.getItem('myKey');
console.log(data); // 'myValue'
sessionStorage.setItem('sessionKey', 'sessionValue');
let data = sessionStorage.getItem('sessionKey');
console.log(data); // 'sessionValue'
<script>
let div = document.getElementById('myDiv');
let customData = div.dataset.myAttr;
console.log(customData); // 'hello'
</script>
let a = 5;
let b = 10;
(function() {
console.log("This is an IIFE!");
})();
49- Strict Mode: Strict mode makes several changes to normal JavaScript
semantics. It eliminates some JavaScript silent errors by changing them
to throw errors.
'use strict';
x = 3.14; // This will cause an error because x is not defined
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5, 2)); // 10
console.log(multiply(5)); // 5
let pos = 0;
let box = document.getElementById("animate");
55- JavaScript BOM (Browser Object Model): The BOM allows JavaScript to
"talk to" the browser, it includes objects like navigator, history,
screen, location and document which is also the entry point into the
web page's content.
56- Web Workers: Web Workers are a simple means for web content to run
scripts in background threads.
fetch('https://api.github.com/users/github')
.then(response => response.json())
.then(data => console.log(data));
59- Object Property Shorthand: In situations where the key and the
value that you're assigning to the key in the object you're creating
are the same, you can use a shorthand to create properties.
61- WeakSet: The WeakSet object lets you store weakly held objects in a
collection.
66- Async Iterators and Generators: They are enable the async functions
to be paused in the middle, one line at a time, and resumed only when a
value is ready, perfect for working with streams and other asynchronous
data sources.
let height = 0;
console.log(height ?? 100); // 0
customElements.define('my-element', MyElement);
72- Shadow DOM: Encapsulates style and structure for web components.
this.handleClick = this.handleClick.bind(this);
74- GlobalThis: A universal way to access the global this value (aka
global object) across environments.
76- Array at() method: Allows to get the element at a given index, with
support for negative indices.
match (value) {
when ({ a: 1, b }) -> b
else -> throw new Error('not matched')
}
function multiply(a) {
return function(b) {
return a * b;
};
}
82- Currying with lodash: The curry function from lodash can be used
for currying.
const _ = require('lodash');
function multiply(a, b, c) {
return a * b * c;
}
function add(a) {
return a + 1;
}
function multiply(b) {
return b * 2;
}
if (n <= 2) {
return 1;
}
console.log(fibonacci(10)); // Output: 55
85- Proxy traps: Proxy traps are the methods that can be defined on the
handler object to customize the behavior of the proxied object.
const handler = {
get(target, property) {
console.log(`Accessed ${property}`);
return target[property];
},
};
function* generateNumbers() {
let number = 0;
while (true) {
yield number++;
}
}
console.log(numberGenerator.next().value); // Output: 0
console.log(numberGenerator.next().value); // Output: 1
87- Private class fields: Private class fields are class fields that are
scoped to the class and cannot be accessed outside of it.
class Person {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
const user = {
name: 'John',
address: {
city: 'New York',
},
};
90- Web Workers: Web Workers allow running JavaScript code in the
background, off the main thread, to improve performance and
responsiveness.
// Main thread
const worker = new Worker('worker.js');
92- Custom iterable objects: You can create custom iterable objects by
implementing the iterator protocol.
const iterable = {
items: ['a', 'b', 'c'],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.items.length) {
return { value: this.items[index++], done: false };
}
return { done: true };
},
};
},
};
return descriptor;
}
class Calculator {
@log
add(a, b) {
return a + b;
}
}
function logMessage() {
console.log('Message logged');
}
function saveData() {
console.log('Data saved');
}
const obj = {
name: 'John',
age: 30,
};
Object.freeze(obj);
obj.age = 40; // Assignment is ignored in strict mode or throws an error in
non-strict mode
console.log(obj.age); // Output: 30
const obj = {
name: 'John',
age: 30,
};
Object.seal(obj);
delete obj.age; // Deletion is ignored
obj.name = 'Jane'; // Property can be modified
obj.gender = 'Male'; // Property addition is ignored
const obj = {
name: 'John',
age: 30,
};
Object.preventExtensions(obj);
obj.name = 'Jane'; // Property can be modified
obj.gender = 'Male'; // Property addition is ignored
99- FlatMap: The flatMap method combines the map and flat methods,
allowing mapping each element to a new array and then flattening the
resulting arrays into a single array.
const entries = [
['name', 'John'],
['age', 30],
];
const sentence = 'The quick brown fox jumps over the lazy dog.';
const newSentence = sentence.replaceAll('the', 'a');
console.log(newSentence); // Output: The quick brown fox jumps over a lazy
dog.
const obj = {
name: 'John',
};
105- File API: The File API provides a way to interact with files on the
user's device using JavaScript.
reader.readAsText(file);
});
const options = {
root: null,
rootMargin: '0px',
threshold: 0.5,
};
Before we get into OOP with JavaScript, let's review some JavaScript
fundamentals. If you're already comfortable with these concepts, feel
free to move to the next section.
2.1 Syntax
let x = 10;
const y = 20;
In addition to these, JavaScript has the Object data type which can
store collections of data.
function sayHello() {
console.log("Hello, world!");
}
if (x > y) {
console.log("x is greater than y");
} else {
console.log("x is not greater than y");
}
let dog = {
name: "Spot",
breed: "Dalmatian",
age: 3,
bark: function() {
console.log("Woof!");
}
};
Using Object.create():
dog.bark = function() {
console.log("Woof!");
};
Object.create(proto, [propertiesObject])
You can access properties and methods on an object using dot notation
or bracket notation:
And you can delete properties from an object using the delete keyword:
delete dog.age;
console.log(dog.age); // outputs: undefined
You can then create a new Dog object using the new keyword:
let animal = {
species: "animal",
describe: function() {
return `This is a ${this.species}.`;
}
};
function Animal(species) {
this.species = species;
}
Animal.prototype.describe = function() {
return `This is a ${this.species}.`;
};
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
Then we add a bark method to the Dog prototype. This method is specific
to Dog instances and is not shared by Animal instances.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.height * this.width;
}
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
Section 7: Encapsulation
Here is an example:
class Car {
constructor(make, model) {
this._make = make;
this._model = model;
this._odometer = 0;
}
get make() {
return this._make;
}
get model() {
return this._model;
}
drive(distance) {
this._odometer += distance;
}
readOdometer() {
return this._odometer;
}
}
Keep in mind that this isn't true privacy. The properties can still be
accessed and modified directly, and the underscore is just a convention
to signal that they shouldn't be. ES6 introduced a new feature,
JavaScript #private fields, to make properties truly private.
class BankAccount {
#balance;
constructor(initialDeposit) {
this.#balance = initialDeposit;
}
deposit(amount) {
this.#balance += amount;
}
withdraw(amount) {
if (this.#balance >= amount) {
this.#balance -= amount;
getBalance() {
return this.#balance;
}
}
As you can see in this example, attempting to access the #balance field
directly from outside the class results in a syntax error. This ensures
that the #balance field can only be accessed or modified through the
deposit, withdraw, and getBalance methods.
Section 8: Polymorphism
Here's an example:
In this example, the Dog and Cat classes override the makeSound method
of the Animal class. When makeSound is called on a Dog or Cat object,
the overridden method in the respective child class is executed.
Sometimes, you might want to execute the parent class's method before
or after executing additional code in the child class's method. You can
do this using the super keyword.
Here's an example:
Section 9: Abstraction
class Vehicle {
constructor(name, type) {
this.name = name;
this.type = type;
}
start() {
return `${this.name} engine started`;
}
}
class BankAccount {
constructor(balance = 0) {
this.balance = balance;
}
deposit(amount) {
this.balance += amount;
return this.balance;
}
withdraw(amount) {
if (amount > this.balance) {
return "Insufficient funds";
} else {
this.balance -= amount;
return this.balance;
}
}
}
Composition and inheritance are two ways to reuse code across objects
in JavaScript.
const canEat = {
eat: function() {
console.log("Eating");
}
};
const canWalk = {
walk: function() {
console.log("Walking");
}
};
In this example, we're composing a person object from the canEat and
canWalk objects, rather than inheriting from a parent class.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
In each case, we're achieving the same end result: we're creating a
person object with a name and age. The method you choose to create
objects really depends on your specific needs and the conventions of
your codebase.
// mathFunctions.js
export function add(x, y) {
return x + y;
}
// MyModule.js
export default function() { console.log("I'm the default export!"); }
You can use the import keyword to import functions, objects, or values
that were exported from another module.
// main.js
import { add, subtract } from './mathFunctions.js';
console.log(add(2, 2)); // 4
console.log(subtract(2, 2)); // 0
// main.js
import myDefaultFunction from './MyModule.js';
Design patterns are not to be memorized and forced into your code.
Instead, they should be considered tools that can be used when they are
needed. They are language-independent strategies for solving common
object-oriented design problems. That means you can use them in almost
any language that supports object-oriented programming concepts.
1. Singleton Pattern
2. Factory Pattern
3. Abstract Factory Pattern
4. Builder Pattern
5. Prototype Pattern
1. Singleton Pattern
class Singleton {
constructor() {
if (!instance) {
instance = this;
}
return instance;
}
}
class CarFactory {
createCar(model) {
if (model === 'Yaris') return new Yaris();
if (model === 'Prius') return new Prius();
return null;
}
}
class Yaris {
info() {
return "This is a Yaris";
}
}
class Prius {
info() {
return "This is a Prius";
}
}
getVehicle(type, options) {
const Vehicle = this.types[type];
return (Vehicle) ? new Vehicle(options) : null;
}
registerVehicle(type, Vehicle) {
const proto = Vehicle.prototype;
return this;
}
}
class Car {
constructor(options) {
this.doors = options.doors || 4;
this.state = options.state || "brand new";
}
drive() {}
breakDown() {}
}
class Truck {
constructor(options) {
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
}
drive() {}
breakDown() {}
}
vehicleFactory.registerVehicle("car", Car);
console.log(car);
console.log(truck);
4. Builder Pattern
class CarBuilder {
constructor() {
this.car = null;
}
step1() {
this.car = new Car();
}
step2() {
this.car.addParts();
}
get() {
return this.car;
}
}
class Car {
constructor() {
this.doors = 0;
}
addParts() {
this.doors = 4;
}
say() {
5. Prototype Pattern
class Car {
constructor(model, color) {
this.model = model;
this.color = color;
}
clone() {
return new Car(this.model, this.color);
}
}
These are the main types of creational patterns, each used in different
scenarios. Understanding the difference between them and when to use
which is important, and that comes with time and practice. In the next
part, we will go over Structural Design Patterns.
These patterns deal with object composition and ensure that different
parts of a system work together in a structured and efficient manner.
1. Adapter
2. Decorator
3. Facade
4. Composite
5. Proxy
1. Adapter Pattern
class Logger {
log(message) {
console.log(message);
}
}
class Filesystem {
write(file, data) {
console.log(`Writing "${data}" to file ${file}`);
// Actual file writing code goes here
}
}
log(message) {
const file = '/path/to/log/file';
this.fs.write(file, message);
}
}
2. Decorator Pattern
Imagine you're building a coffee order system, and you need to allow
customers to add optional extras like milk, sugar, and whip to their
orders.
class Coffee {
cost() {
return 1;
}
}
Here, Milk, Sugar, and Whip are decorators for the Coffee class. Each
one adds its own behavior to the Coffee's cost method.
Imagine you have a complex subsystem for handling customer orders, with
separate classes for order creation, inventory checking, payment
processing, and shipping. You could use a facade to simplify this:
class OrderSubsystem {
placeOrder() {
console.log('Order placed.');
}
}
class InventorySubsystem {
checkInventory() {
console.log('Inventory checked.');
}
}
class PaymentSubsystem {
processPayment() {
console.log('Payment processed.');
}
}
class ShippingSubsystem {
shipOrder() {
console.log('Order shipped.');
}
}
class OrderFacade {
constructor() {
this.orderSubsystem = new OrderSubsystem();
this.inventorySubsystem = new InventorySubsystem();
this.paymentSubsystem = new PaymentSubsystem();
this.shippingSubsystem = new ShippingSubsystem();
}
4. Composite Pattern
class File {
constructor(name) {
this.name = name;
}
display() {
console.log(this.name);
}
}
class Directory {
constructor(name) {
this.name = name;
this.children = [];
}
remove(child) {
const index = this.children.indexOf(child);
if (index > -1) {
this.children.splice(index, 1);
}
}
display(indentation = '') {
console.log(`${indentation}${this.name}`);
for (const child of this.children) {
child.display(`${indentation} `);
}
}
}
root.add(home);
root.add(conf);
home.add(readme);
root.display();
In this example, File and Directory are composites. They both have a
display method, but Directory also has add and remove methods for
managing child nodes.
5. Proxy Pattern
class HeavyDatabase {
constructor() {
console.log('Creating HeavyDatabase instance...');
// Lots of initialization code...
}
getStatus() {
return 'OK';
}
}
class DatabaseProxy {
getStatus() {
if (!this.database) {
this.database = new HeavyDatabase();
}
return this.database.getStatus();
}
}
Remember, these are simplified examples and real-world use cases can be
more complex. The key to understanding design patterns is to grasp the
"why" behind using them. In the next part, we will discuss some
exercises to help you understand these patterns better.
1. Strategy Pattern
class PaymentStrategy {
static Card(user) {
return `User ${user} will pay using Credit Card`;
}
static Paypal(user) {
return `User ${user} will pay using PayPal`;
}
static Bitcoin(user) {
return `User ${user} will pay using Bitcoin`;
}
}
class Shopping {
constructor(strategy = 'Card') {
this.strategy = PaymentStrategy[strategy];
}
changeStrategy(newStrategy) {
this.strategy = PaymentStrategy[newStrategy];
}
checkout(user) {
return this.strategy(user);
}
}
shopping.changeStrategy('Paypal');
console.log(shopping.checkout('John')); // User John will pay using PayPal
2. Observer Pattern
class Newsletter {
constructor() {
this.subscribers = [];
}
subscribe(observer) {
this.subscribers.push(observer);
}
unsubscribe(observer) {
const index = this.subscribers.indexOf(observer);
if (index > -1) {
this.subscribers.splice(index, 1);
}
}
notify(data) {
for (let i = 0; i < this.subscribers.length; i++) {
this.subscribers[i](data);
}
}
}
newsletter.subscribe(john);
newsletter.subscribe(jane);
newsletter.unsubscribe(john);
newsletter.notify('Second edition out now!');
// Message to Jane: Second edition out now!
3. Command Pattern
class Calculator {
constructor() {
this.value = 0;
}
execute(command) {
this.value = command.execute(this.value);
}
undo(command) {
this.value = command.undo(this.value);
}
}
class AddCommand {
constructor(valueToAdd) {
this.valueToAdd = valueToAdd;
}
undo(currentValue) {
return currentValue - this.valueToAdd;
}
}
calculator.execute(addFiveCommand);
console.log(calculator.value); // 5
calculator.undo(addFiveCommand);
console.log(calculator.value); // 0
4. Iterator Pattern
class ArrayIterator {
constructor(arr) {
this.array = arr;
this.index = 0;
}
hasNext() {
return this.index < this.array.length;
}
next() {
return this.array[this.index++];
}
while(iterator.hasNext()) {
console.log(iterator.next());
}
5. State Pattern
class TrafficLight {
constructor() {
this.states = [new GreenLight(), new YellowLight(), new RedLight()];
this.current = this.states[0];
}
change() {
const totalStates = this.states.length;
let currentIndex = this.states.findIndex(light => light ===
this.current);
sign() {
return this.current.message;
}
}
console.log(trafficLight.sign()); // Go
trafficLight.change();
console.log(trafficLight.sign()); // Careful
trafficLight.change();
console.log(trafficLight.sign()); // Stop
class Builder {
build() {
this.test();
this.lint();
this.assemble();
this.deploy();
}
test() {
throw new Error('You have to implement the method test!');
}
lint() {
throw new Error('You have to implement the method lint!');
}
assemble() {
throw new Error('You have to implement the method assemble!');
}
deploy() {
throw new Error('You have to implement the method deploy!');
}
}
lint() {
console.log('Linting the javascript code');
}
assemble() {
console.log('Assembling the javascript build');
}
deploy() {
console.log('Deploying javascript build to server');
}
}
// Output:
// Running javascript tests
// Linting the javascript code
// Assembling the javascript build
// Deploying javascript build to server
7. Visitor Pattern
class Monkey {
shout() {
console.log('Ooh oo aa aa!');
}
accept(operation) {
operation.visitMonkey(this);
}
}
class Lion {
roar() {
console.log('Roaaar!');
}
accept(operation) {
operation.visitLion(this);
}
}
class Dolphin {
speak() {
console.log('Tuut tuttu tuutt!');
accept(operation) {
operation.visitDolphin(this);
}
}
class Speak {
visitMonkey(monkey) {
monkey.shout();
}
visitLion(lion) {
lion.roar();
}
visitDolphin(dolphin) {
dolphin.speak();
}
}
In this example, Monkey, Lion and Dolphin all accept an operation and
call the corresponding method of the operation on themselves.
Final Notes:
To get a feel for how these patterns work together, consider this
scenario:
┣ TRACE : Returns the full HTTP request received by the server for
debugging and diagnostic purposes
┣ OPTIONS : Returns the HTTP methods supported by the server for the
requested URL
┣ 2xx : Success
┣ 3xx : Redirection
┣ Server : Specifies the name and version of the server software that
generated the response
┣ Expires : Specifies the date and time after which the response is
considered stale
┗ Last-Modified : Specifies the date and time the resource was last
modified.
API Design
┣ REST : Representational State Transfer, a design pattern for
building web services
API Testing
┣ Postman : A popular tool for testing and debugging APIs
API Development
┣ Node.js : A JavaScript runtime for building server-side
applications
┣ Amazon API Gateway : A fully managed service that makes it easy for
developers to create, publish, maintain, monitor, and secure APIs
API Performance
┣ Caching : A technique for improving API performance by storing
responses in a cache
┣ New Relic : A tool for monitoring the performance of APIs and other
web applications
API Standards
┣ JSON API : A specification for building APIs that use JSON as the
data format
┗ JSON API : A specification for building APIs that use JSON as the
data format.
API Infrastructure
┣ Kubernetes : An open-source platform for managing containerized
workloads and services
API Documentation
┣ OpenAPI : A specification for building APIs in YAML or JSON format
API Deployment
┣ Heroku : A cloud platform for deploying, managing, and scaling web
applications and APIs
API Security
┣ OAuth : An open standard for authorization used by many social
media platforms and APIs
API Tutorials
┣ Getting Started with RESTful APIs by Tania Rascia
┣ Testing RESTful Web Services Made Easy Using the REST Assured
Framework by Dinesh Rajput
API Tools
┣ API Studio : A web-based IDE for designing and testing APIs
Repository Setup
┣ Create new repository: git init
Basic Workflow
┣ Check status: git status
Stashing
┣ Save changes to stash: git stash save "Message"
Tagging
┣ Create annotated tag: git tag -a <tag-name> -m "Message"
┣ Discard all changes since last commit: git reset --hard HEAD
Miscellaneous
┣ Show commit history: git log
Git Flow
┣ Initialize Git Flow: git flow init
Git Hooks
┣ Client-side hooks: .git/hooks/
Advanced Workflow
┣ Rebase: git rebase <branch>
┣ SourceTree
┣ GitKraken
┣ Git Extensions
┗ TortoiseGit
┗ Show who last modified each line of a file: git blame <file>
Best Practices
┣ Keep commits small and focused
Resources
┣ Official Git website: https://git-scm.com/