mirror of
https://github.com/donnemartin/system-design-primer.git
synced 2025-12-16 09:58:56 +03:00
Move images and solution under docs
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design a call center"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* What levels of employees are in the call center?\n",
|
||||
" * Operator, supervisor, director\n",
|
||||
"* Can we assume operators always get the initial calls?\n",
|
||||
" * Yes\n",
|
||||
"* If there is no available operators or the operator can't handle the call, does the call go to the supervisors?\n",
|
||||
" * Yes\n",
|
||||
"* If there is no available supervisors or the supervisor can't handle the call, does the call go to the directors?\n",
|
||||
" * Yes\n",
|
||||
"* Can we assume the directors can handle all calls?\n",
|
||||
" * Yes\n",
|
||||
"* What happens if nobody can answer the call?\n",
|
||||
" * It gets queued\n",
|
||||
"* Do we need to handle 'VIP' calls where we put someone to the front of the line?\n",
|
||||
" * No\n",
|
||||
"* Can we assume inputs are valid or do we have to validate them?\n",
|
||||
" * Assume they're valid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting call_center.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile call_center.py\n",
|
||||
"from abc import ABCMeta, abstractmethod\n",
|
||||
"from collections import deque\n",
|
||||
"from enum import Enum\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Rank(Enum):\n",
|
||||
"\n",
|
||||
" OPERATOR = 0\n",
|
||||
" SUPERVISOR = 1\n",
|
||||
" DIRECTOR = 2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Employee(metaclass=ABCMeta):\n",
|
||||
"\n",
|
||||
" def __init__(self, employee_id, name, rank, call_center):\n",
|
||||
" self.employee_id = employee_id\n",
|
||||
" self.name = name\n",
|
||||
" self.rank = rank\n",
|
||||
" self.call = None\n",
|
||||
" self.call_center = call_center\n",
|
||||
"\n",
|
||||
" def take_call(self, call):\n",
|
||||
" \"\"\"Assume the employee will always successfully take the call.\"\"\"\n",
|
||||
" self.call = call\n",
|
||||
" self.call.employee = self\n",
|
||||
" self.call.state = CallState.IN_PROGRESS\n",
|
||||
"\n",
|
||||
" def complete_call(self):\n",
|
||||
" self.call.state = CallState.COMPLETE\n",
|
||||
" self.call_center.notify_call_completed(self.call)\n",
|
||||
"\n",
|
||||
" @abstractmethod\n",
|
||||
" def escalate_call(self):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
" def _escalate_call(self):\n",
|
||||
" self.call.state = CallState.READY\n",
|
||||
" call = self.call\n",
|
||||
" self.call = None\n",
|
||||
" self.call_center.notify_call_escalated(call)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Operator(Employee):\n",
|
||||
"\n",
|
||||
" def __init__(self, employee_id, name):\n",
|
||||
" super(Operator, self).__init__(employee_id, name, Rank.OPERATOR)\n",
|
||||
"\n",
|
||||
" def escalate_call(self):\n",
|
||||
" self.call.rank = Rank.SUPERVISOR\n",
|
||||
" self._escalate_call()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Supervisor(Employee):\n",
|
||||
"\n",
|
||||
" def __init__(self, employee_id, name):\n",
|
||||
" super(Operator, self).__init__(employee_id, name, Rank.SUPERVISOR)\n",
|
||||
"\n",
|
||||
" def escalate_call(self):\n",
|
||||
" self.call.rank = Rank.DIRECTOR\n",
|
||||
" self._escalate_call()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Director(Employee):\n",
|
||||
"\n",
|
||||
" def __init__(self, employee_id, name):\n",
|
||||
" super(Operator, self).__init__(employee_id, name, Rank.DIRECTOR)\n",
|
||||
"\n",
|
||||
" def escalate_call(self):\n",
|
||||
" raise NotImplemented('Directors must be able to handle any call')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class CallState(Enum):\n",
|
||||
"\n",
|
||||
" READY = 0\n",
|
||||
" IN_PROGRESS = 1\n",
|
||||
" COMPLETE = 2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Call(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, rank):\n",
|
||||
" self.state = CallState.READY\n",
|
||||
" self.rank = rank\n",
|
||||
" self.employee = None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class CallCenter(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, operators, supervisors, directors):\n",
|
||||
" self.operators = operators\n",
|
||||
" self.supervisors = supervisors\n",
|
||||
" self.directors = directors\n",
|
||||
" self.queued_calls = deque()\n",
|
||||
"\n",
|
||||
" def dispatch_call(self, call):\n",
|
||||
" if call.rank not in (Rank.OPERATOR, Rank.SUPERVISOR, Rank.DIRECTOR):\n",
|
||||
" raise ValueError('Invalid call rank: {}'.format(call.rank))\n",
|
||||
" employee = None\n",
|
||||
" if call.rank == Rank.OPERATOR:\n",
|
||||
" employee = self._dispatch_call(call, self.operators)\n",
|
||||
" if call.rank == Rank.SUPERVISOR or employee is None:\n",
|
||||
" employee = self._dispatch_call(call, self.supervisors)\n",
|
||||
" if call.rank == Rank.DIRECTOR or employee is None:\n",
|
||||
" employee = self._dispatch_call(call, self.directors)\n",
|
||||
" if employee is None:\n",
|
||||
" self.queued_calls.append(call)\n",
|
||||
"\n",
|
||||
" def _dispatch_call(self, call, employees):\n",
|
||||
" for employee in employees:\n",
|
||||
" if employee.call is None:\n",
|
||||
" employee.take_call(call)\n",
|
||||
" return employee\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
" def notify_call_escalated(self, call): # ...\n",
|
||||
" def notify_call_completed(self, call): # ...\n",
|
||||
" def dispatch_queued_call_to_newly_freed_employee(self, call, employee): # ..."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.4.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
142
docs/solutions/object_oriented_design/call_center/call_center.py
Normal file
142
docs/solutions/object_oriented_design/call_center/call_center.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections import deque
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Rank(Enum):
|
||||
|
||||
OPERATOR = 0
|
||||
SUPERVISOR = 1
|
||||
DIRECTOR = 2
|
||||
|
||||
|
||||
class Employee(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, employee_id, name, rank):
|
||||
self.employee_id = employee_id
|
||||
self.name = name
|
||||
self.rank = rank
|
||||
self.call = None
|
||||
self.call_center = None
|
||||
|
||||
def take_call(self, call):
|
||||
"""Assume the employee will always successfully take the call."""
|
||||
self.call = call
|
||||
self.call.employee = self
|
||||
self.call.state = CallState.IN_PROGRESS
|
||||
|
||||
def complete_call(self):
|
||||
assert self.call_center is not None
|
||||
self.call.state = CallState.COMPLETE
|
||||
self.call_center.notify_call_completed(self.call)
|
||||
|
||||
@abstractmethod
|
||||
def escalate_call(self):
|
||||
pass
|
||||
|
||||
def _escalate_call(self):
|
||||
assert self.call_center is not None
|
||||
self.call.state = CallState.READY
|
||||
call = self.call
|
||||
self.call = None
|
||||
self.call_center.notify_call_escalated(call)
|
||||
|
||||
def set_call_center(self, call_center):
|
||||
self.call_center = call_center
|
||||
|
||||
|
||||
class Operator(Employee):
|
||||
|
||||
def __init__(self, employee_id, name):
|
||||
super(Operator, self).__init__(employee_id, name, Rank.OPERATOR)
|
||||
|
||||
def escalate_call(self):
|
||||
self.call.level = Rank.SUPERVISOR
|
||||
self._escalate_call()
|
||||
|
||||
|
||||
class Supervisor(Employee):
|
||||
|
||||
def __init__(self, employee_id, name):
|
||||
super(Supervisor, self).__init__(employee_id, name, Rank.SUPERVISOR)
|
||||
|
||||
def escalate_call(self):
|
||||
self.call.level = Rank.DIRECTOR
|
||||
self._escalate_call()
|
||||
|
||||
|
||||
class Director(Employee):
|
||||
|
||||
def __init__(self, employee_id, name):
|
||||
super(Director, self).__init__(employee_id, name, Rank.DIRECTOR)
|
||||
|
||||
def escalate_call(self):
|
||||
raise NotImplementedError('Directors must be able to handle any call')
|
||||
|
||||
|
||||
class CallState(Enum):
|
||||
|
||||
READY = 0
|
||||
IN_PROGRESS = 1
|
||||
COMPLETE = 2
|
||||
|
||||
|
||||
class Call(object):
|
||||
|
||||
def __init__(self, rank):
|
||||
self.state = CallState.READY
|
||||
self.rank = rank
|
||||
self.employee = None
|
||||
|
||||
|
||||
class CallCenter(object):
|
||||
|
||||
def __init__(self, operators, supervisors, directors):
|
||||
self.operators = []
|
||||
self.supervisors = []
|
||||
self.directors = []
|
||||
self.queued_calls = deque()
|
||||
for employee in operators + supervisors + directors:
|
||||
self.add_employee(employee)
|
||||
|
||||
def dispatch_call(self, call):
|
||||
if call.rank not in (Rank.OPERATOR, Rank.SUPERVISOR, Rank.DIRECTOR):
|
||||
raise ValueError('Invalid call rank: {}'.format(call.rank))
|
||||
employee = None
|
||||
if call.rank == Rank.OPERATOR:
|
||||
employee = self._dispatch_call(call, self.operators)
|
||||
if call.rank == Rank.SUPERVISOR or employee is None:
|
||||
employee = self._dispatch_call(call, self.supervisors)
|
||||
if call.rank == Rank.DIRECTOR or employee is None:
|
||||
employee = self._dispatch_call(call, self.directors)
|
||||
|
||||
if employee is None:
|
||||
self.queued_calls.append(call)
|
||||
|
||||
def _dispatch_call(self, call, employees):
|
||||
for employee in employees:
|
||||
if employee.call is None:
|
||||
employee.take_call(call)
|
||||
return employee
|
||||
return None
|
||||
|
||||
def add_employee(self, employee: Employee):
|
||||
if isinstance(employee, Operator):
|
||||
self.operators.append(employee)
|
||||
elif isinstance(employee, Supervisor):
|
||||
self.supervisors.append(employee)
|
||||
elif isinstance(employee, Director):
|
||||
self.directors.append(employee)
|
||||
else:
|
||||
raise ValueError('Invalid employee type: {}'.format(type(employee)))
|
||||
employee.set_call_center(self)
|
||||
|
||||
|
||||
def notify_call_escalated(self, call):
|
||||
pass
|
||||
|
||||
def notify_call_completed(self, call):
|
||||
pass
|
||||
|
||||
def dispatch_queued_call_to_newly_freed_employee(self, call, employee):
|
||||
pass
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
# test_call_center.py - Generated by CodiumAI
|
||||
|
||||
import pytest
|
||||
from call_center.call_center import Operator, Supervisor, Director, Call, CallCenter, Rank
|
||||
|
||||
"""
|
||||
Code Analysis:
|
||||
- The CallCenter class is used to manage calls in a call center.
|
||||
- It takes three parameters: operators, supervisors, and directors.
|
||||
- The queued_calls attribute is a deque object, which is a double-ended queue.
|
||||
- The dispatch_call() method is used to assign a call to an employee based on the call's rank.
|
||||
- If an employee is not available, the call is added to the queued_calls deque.
|
||||
- The _dispatch_call() method is used to assign a call to an employee.
|
||||
- The notify_call_escalated() and notify_call_completed() methods are used to notify the call center when a call is escalated or completed.
|
||||
- The dispatch_queued_call_to_newly_freed_employee() method is used to assign a queued call to an employee who has just become available.
|
||||
"""
|
||||
|
||||
"""
|
||||
Test Plan:
|
||||
- test_dispatch_call(): tests that the dispatch_call() method assigns the call to the correct employee based on the call's rank
|
||||
- test_dispatch_call_with_no_available_employee(): tests that the dispatch_call() method adds the call to the queued_calls deque if no employee is available
|
||||
- test_add_employee(): tests that the add_employee() method adds the employee to the correct list based on the employee's type
|
||||
|
||||
"""
|
||||
|
||||
class TestCallCenter():
|
||||
|
||||
def setup_method(self, method):
|
||||
self.operators = [Operator(1, 'John'), Operator(2, 'Jane')]
|
||||
self.supervisors = [Supervisor(3, 'Bob')]
|
||||
self.directors = [Director(4, 'Alice')]
|
||||
self.call_center = CallCenter(self.operators, self.supervisors, self.directors)
|
||||
|
||||
def test_dispatch_call(self):
|
||||
call = Call(Rank.OPERATOR)
|
||||
self.call_center.dispatch_call(call)
|
||||
assert self.operators[0].call == call
|
||||
|
||||
call = Call(Rank.SUPERVISOR)
|
||||
self.call_center.dispatch_call(call)
|
||||
assert self.supervisors[0].call == call
|
||||
|
||||
call = Call(Rank.DIRECTOR)
|
||||
self.call_center.dispatch_call(call)
|
||||
assert self.directors[0].call == call
|
||||
|
||||
def test_dispatch_call_with_no_available_employee(self):
|
||||
for employee in self.operators + self.supervisors + self.directors:
|
||||
employee.take_call(Call(Rank.OPERATOR))
|
||||
assert employee.call is not None
|
||||
|
||||
call = Call(Rank.OPERATOR)
|
||||
self.call_center.dispatch_call(call)
|
||||
assert len(self.call_center.queued_calls) == 1
|
||||
|
||||
|
||||
def test_add_employee(self):
|
||||
operator = Operator(5, 'Tom')
|
||||
supervisor = Supervisor(6, 'Jerry')
|
||||
director = Director(7, 'Sally')
|
||||
|
||||
self.call_center.add_employee(operator)
|
||||
assert operator in self.call_center.operators
|
||||
|
||||
self.call_center.add_employee(supervisor)
|
||||
assert supervisor in self.call_center.supervisors
|
||||
|
||||
self.call_center.add_employee(director)
|
||||
assert director in self.call_center.directors
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Abhijeet Antin](https://github.com/abhijeet14antin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design a circular array"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"jp-MarkdownHeadingCollapsed": true
|
||||
},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* What are we queueing?\n",
|
||||
" * We are queueing integers\n",
|
||||
"* Can we assume inputs are valid or do we have to validate them?\n",
|
||||
" * Assume they're valid\n",
|
||||
"* Can we assume this fits memory?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting circular_array.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile circular_array.py\n",
|
||||
"\n",
|
||||
"class CircularArray(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, MAX_SIZE):\n",
|
||||
" self.MAX_SIZE = MAX_SIZE\n",
|
||||
" self.size = 0\n",
|
||||
" self.array = [None] * MAX_SIZE\n",
|
||||
" self.front = 0\n",
|
||||
" self.back = 0\n",
|
||||
"\n",
|
||||
" def push(self, value):\n",
|
||||
" \"\"\"Push the value to the back of the circular array\n",
|
||||
" \"\"\"\n",
|
||||
" if self.size == self.MAX_SIZE:\n",
|
||||
" raise IndexError('Array is full')\n",
|
||||
" self.array[self.back] = value\n",
|
||||
" self.back += 1\n",
|
||||
" self.size += 1\n",
|
||||
" \n",
|
||||
" # wrap around\n",
|
||||
" if self.back == self.MAX_SIZE:\n",
|
||||
" self.back = 0\n",
|
||||
"\n",
|
||||
" def pop(self):\n",
|
||||
" \"\"\"Pop a value from the front of the circular array\n",
|
||||
" \"\"\"\n",
|
||||
" if self.size == 0:\n",
|
||||
" raise IndexError('Array is empty')\n",
|
||||
" ret = self.array[self.front]\n",
|
||||
" self.front += 1\n",
|
||||
" self.size -= 1\n",
|
||||
"\n",
|
||||
" # wrap around\n",
|
||||
" if self.front == self.MAX_SIZE:\n",
|
||||
" self.front = 0\n",
|
||||
" return ret\n",
|
||||
" \n",
|
||||
" def print_array(self):\n",
|
||||
" print('Array: ', end = ' ')\n",
|
||||
" if self.size == 0:\n",
|
||||
" print('empty')\n",
|
||||
" return\n",
|
||||
" i = self.front\n",
|
||||
" while True:\n",
|
||||
" print(self.array[i], end = ' ')\n",
|
||||
" i += 1\n",
|
||||
" if i == self.MAX_SIZE:\n",
|
||||
" i = 0\n",
|
||||
" if i == self.back:\n",
|
||||
" print()\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Array: empty\n",
|
||||
"Array: 1 2 3 4 5 \n",
|
||||
"Array: 3 4 5 \n",
|
||||
"Array: 3 4 5 6 \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from circular_array import CircularArray\n",
|
||||
"ca = CircularArray(5)\n",
|
||||
"ca.print_array()\n",
|
||||
"ca.push(1)\n",
|
||||
"ca.push(2)\n",
|
||||
"ca.push(3)\n",
|
||||
"ca.push(4)\n",
|
||||
"ca.push(5)\n",
|
||||
"ca.print_array()\n",
|
||||
"\n",
|
||||
"ca.pop()\n",
|
||||
"ca.pop()\n",
|
||||
"ca.print_array()\n",
|
||||
"\n",
|
||||
"ca.push(6)\n",
|
||||
"ca.print_array()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
class CircularArray(object):
|
||||
|
||||
def __init__(self, MAX_SIZE):
|
||||
self.MAX_SIZE = MAX_SIZE
|
||||
self.size = 0
|
||||
self.array = [None] * MAX_SIZE
|
||||
self.front = 0
|
||||
self.back = 0
|
||||
|
||||
def push(self, value):
|
||||
"""Push the value to the back of the circular array
|
||||
"""
|
||||
if self.size == self.MAX_SIZE:
|
||||
raise IndexError('Array is full')
|
||||
self.array[self.back] = value
|
||||
self.back += 1
|
||||
self.size += 1
|
||||
|
||||
# wrap around
|
||||
if self.back == self.MAX_SIZE:
|
||||
self.back = 0
|
||||
|
||||
def pop(self):
|
||||
"""Pop a value from the front of the circular array
|
||||
"""
|
||||
if self.size == 0:
|
||||
raise IndexError('Array is empty')
|
||||
ret = self.array[self.front]
|
||||
self.front += 1
|
||||
self.size -= 1
|
||||
|
||||
# wrap around
|
||||
if self.front == self.MAX_SIZE:
|
||||
self.front = 0
|
||||
return ret
|
||||
|
||||
def print_array(self):
|
||||
print('Array: ', end = ' ')
|
||||
if self.size == 0:
|
||||
print('empty')
|
||||
return
|
||||
i = self.front
|
||||
while True:
|
||||
print(self.array[i], end = ' ')
|
||||
i += 1
|
||||
if i == self.MAX_SIZE:
|
||||
i = 0
|
||||
if i == self.back:
|
||||
print()
|
||||
break
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design a deck of cards"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* Is this a generic deck of cards for games like poker and black jack?\n",
|
||||
" * Yes, design a generic deck then extend it to black jack\n",
|
||||
"* Can we assume the deck has 52 cards (2-10, Jack, Queen, King, Ace) and 4 suits?\n",
|
||||
" * Yes\n",
|
||||
"* Can we assume inputs are valid or do we have to validate them?\n",
|
||||
" * Assume they're valid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting deck_of_cards.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile deck_of_cards.py\n",
|
||||
"from abc import ABCMeta, abstractmethod\n",
|
||||
"from enum import Enum\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Suit(Enum):\n",
|
||||
"\n",
|
||||
" HEART = 0\n",
|
||||
" DIAMOND = 1\n",
|
||||
" CLUBS = 2\n",
|
||||
" SPADE = 3\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Card(metaclass=ABCMeta):\n",
|
||||
"\n",
|
||||
" def __init__(self, value, suit):\n",
|
||||
" self.value = value\n",
|
||||
" self.suit = suit\n",
|
||||
" self.is_available = True\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" @abstractmethod\n",
|
||||
" def value(self):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
" @value.setter\n",
|
||||
" @abstractmethod\n",
|
||||
" def value(self, other):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class BlackJackCard(Card):\n",
|
||||
"\n",
|
||||
" def __init__(self, value, suit):\n",
|
||||
" super(BlackJackCard, self).__init__(value, suit)\n",
|
||||
"\n",
|
||||
" def is_ace(self):\n",
|
||||
" return self._value == 1\n",
|
||||
"\n",
|
||||
" def is_face_card(self):\n",
|
||||
" \"\"\"Jack = 11, Queen = 12, King = 13\"\"\"\n",
|
||||
" return 10 < self._value <= 13\n",
|
||||
"\n",
|
||||
" @property\n",
|
||||
" def value(self):\n",
|
||||
" if self.is_ace() == 1:\n",
|
||||
" return 1\n",
|
||||
" elif self.is_face_card():\n",
|
||||
" return 10\n",
|
||||
" else:\n",
|
||||
" return self._value\n",
|
||||
"\n",
|
||||
" @value.setter\n",
|
||||
" def value(self, new_value):\n",
|
||||
" if 1 <= new_value <= 13:\n",
|
||||
" self._value = new_value\n",
|
||||
" else:\n",
|
||||
" raise ValueError('Invalid card value: {}'.format(new_value))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Hand(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, cards):\n",
|
||||
" self.cards = cards\n",
|
||||
"\n",
|
||||
" def add_card(self, card):\n",
|
||||
" self.cards.append(card)\n",
|
||||
"\n",
|
||||
" def score(self):\n",
|
||||
" total_value = 0\n",
|
||||
" for card in self.cards:\n",
|
||||
" total_value += card.value\n",
|
||||
" return total_value\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class BlackJackHand(Hand):\n",
|
||||
"\n",
|
||||
" BLACKJACK = 21\n",
|
||||
"\n",
|
||||
" def __init__(self, cards):\n",
|
||||
" super(BlackJackHand, self).__init__(cards)\n",
|
||||
"\n",
|
||||
" def score(self):\n",
|
||||
" min_over = sys.MAXSIZE\n",
|
||||
" max_under = -sys.MAXSIZE\n",
|
||||
" for score in self.possible_scores():\n",
|
||||
" if self.BLACKJACK < score < min_over:\n",
|
||||
" min_over = score\n",
|
||||
" elif max_under < score <= self.BLACKJACK:\n",
|
||||
" max_under = score\n",
|
||||
" return max_under if max_under != -sys.MAXSIZE else min_over\n",
|
||||
"\n",
|
||||
" def possible_scores(self):\n",
|
||||
" \"\"\"Return a list of possible scores, taking Aces into account.\"\"\"\n",
|
||||
" # ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Deck(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, cards):\n",
|
||||
" self.cards = cards\n",
|
||||
" self.deal_index = 0\n",
|
||||
"\n",
|
||||
" def remaining_cards(self):\n",
|
||||
" return len(self.cards) - deal_index\n",
|
||||
"\n",
|
||||
" def deal_card():\n",
|
||||
" try:\n",
|
||||
" card = self.cards[self.deal_index]\n",
|
||||
" card.is_available = False\n",
|
||||
" self.deal_index += 1\n",
|
||||
" except IndexError:\n",
|
||||
" return None\n",
|
||||
" return card\n",
|
||||
"\n",
|
||||
" def shuffle(self): # ..."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.4.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from enum import Enum
|
||||
import sys
|
||||
|
||||
|
||||
class Suit(Enum):
|
||||
|
||||
HEART = 0
|
||||
DIAMOND = 1
|
||||
CLUBS = 2
|
||||
SPADE = 3
|
||||
|
||||
|
||||
class Card(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, value, suit):
|
||||
self.value = value
|
||||
self.suit = suit
|
||||
self.is_available = True
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def value(self):
|
||||
pass
|
||||
|
||||
@value.setter
|
||||
@abstractmethod
|
||||
def value(self, other):
|
||||
pass
|
||||
|
||||
|
||||
class BlackJackCard(Card):
|
||||
|
||||
def __init__(self, value, suit):
|
||||
super(BlackJackCard, self).__init__(value, suit)
|
||||
|
||||
def is_ace(self):
|
||||
return True if self._value == 1 else False
|
||||
|
||||
def is_face_card(self):
|
||||
"""Jack = 11, Queen = 12, King = 13"""
|
||||
return True if 10 < self._value <= 13 else False
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if self.is_ace() == 1:
|
||||
return 1
|
||||
elif self.is_face_card():
|
||||
return 10
|
||||
else:
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, new_value):
|
||||
if 1 <= new_value <= 13:
|
||||
self._value = new_value
|
||||
else:
|
||||
raise ValueError('Invalid card value: {}'.format(new_value))
|
||||
|
||||
|
||||
class Hand(object):
|
||||
|
||||
def __init__(self, cards):
|
||||
self.cards = cards
|
||||
|
||||
def add_card(self, card):
|
||||
self.cards.append(card)
|
||||
|
||||
def score(self):
|
||||
total_value = 0
|
||||
for card in self.cards:
|
||||
total_value += card.value
|
||||
return total_value
|
||||
|
||||
|
||||
class BlackJackHand(Hand):
|
||||
|
||||
BLACKJACK = 21
|
||||
|
||||
def __init__(self, cards):
|
||||
super(BlackJackHand, self).__init__(cards)
|
||||
|
||||
def score(self):
|
||||
min_over = sys.MAXSIZE
|
||||
max_under = -sys.MAXSIZE
|
||||
for score in self.possible_scores():
|
||||
if self.BLACKJACK < score < min_over:
|
||||
min_over = score
|
||||
elif max_under < score <= self.BLACKJACK:
|
||||
max_under = score
|
||||
return max_under if max_under != -sys.MAXSIZE else min_over
|
||||
|
||||
def possible_scores(self):
|
||||
"""Return a list of possible scores, taking Aces into account."""
|
||||
pass
|
||||
|
||||
|
||||
class Deck(object):
|
||||
|
||||
def __init__(self, cards):
|
||||
self.cards = cards
|
||||
self.deal_index = 0
|
||||
|
||||
def remaining_cards(self):
|
||||
return len(self.cards) - self.deal_index
|
||||
|
||||
def deal_card(self):
|
||||
try:
|
||||
card = self.cards[self.deal_index]
|
||||
card.is_available = False
|
||||
self.deal_index += 1
|
||||
except IndexError:
|
||||
return None
|
||||
return card
|
||||
|
||||
def shuffle(self):
|
||||
pass
|
||||
121
docs/solutions/object_oriented_design/hash_table/hash_map.ipynb
Normal file
121
docs/solutions/object_oriented_design/hash_table/hash_map.ipynb
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design a hash map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* For simplicity, are the keys integers only?\n",
|
||||
" * Yes\n",
|
||||
"* For collision resolution, can we use chaining?\n",
|
||||
" * Yes\n",
|
||||
"* Do we have to worry about load factors?\n",
|
||||
" * No\n",
|
||||
"* Can we assume inputs are valid or do we have to validate them?\n",
|
||||
" * Assume they're valid\n",
|
||||
"* Can we assume this fits memory?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting hash_map.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile hash_map.py\n",
|
||||
"class Item(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, key, value):\n",
|
||||
" self.key = key\n",
|
||||
" self.value = value\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class HashTable(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, size):\n",
|
||||
" self.size = size\n",
|
||||
" self.table = [[] for _ in range(self.size)]\n",
|
||||
"\n",
|
||||
" def _hash_function(self, key):\n",
|
||||
" return key % self.size\n",
|
||||
"\n",
|
||||
" def set(self, key, value):\n",
|
||||
" hash_index = self._hash_function(key)\n",
|
||||
" for item in self.table[hash_index]:\n",
|
||||
" if item.key == key:\n",
|
||||
" item.value = value\n",
|
||||
" return\n",
|
||||
" self.table[hash_index].append(Item(key, value))\n",
|
||||
"\n",
|
||||
" def get(self, key):\n",
|
||||
" hash_index = self._hash_function(key)\n",
|
||||
" for item in self.table[hash_index]:\n",
|
||||
" if item.key == key:\n",
|
||||
" return item.value\n",
|
||||
" raise KeyError('Key not found')\n",
|
||||
"\n",
|
||||
" def remove(self, key):\n",
|
||||
" hash_index = self._hash_function(key)\n",
|
||||
" for index, item in enumerate(self.table[hash_index]):\n",
|
||||
" if item.key == key:\n",
|
||||
" del self.table[hash_index][index]\n",
|
||||
" return\n",
|
||||
" raise KeyError('Key not found')"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.4.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
38
docs/solutions/object_oriented_design/hash_table/hash_map.py
Normal file
38
docs/solutions/object_oriented_design/hash_table/hash_map.py
Normal file
@@ -0,0 +1,38 @@
|
||||
class Item(object):
|
||||
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
class HashTable(object):
|
||||
|
||||
def __init__(self, size):
|
||||
self.size = size
|
||||
self.table = [[] for _ in range(self.size)]
|
||||
|
||||
def _hash_function(self, key):
|
||||
return key % self.size
|
||||
|
||||
def set(self, key, value):
|
||||
hash_index = self._hash_function(key)
|
||||
for item in self.table[hash_index]:
|
||||
if item.key == key:
|
||||
item.value = value
|
||||
return
|
||||
self.table[hash_index].append(Item(key, value))
|
||||
|
||||
def get(self, key):
|
||||
hash_index = self._hash_function(key)
|
||||
for item in self.table[hash_index]:
|
||||
if item.key == key:
|
||||
return item.value
|
||||
raise KeyError('Key not found')
|
||||
|
||||
def remove(self, key):
|
||||
hash_index = self._hash_function(key)
|
||||
for index, item in enumerate(self.table[hash_index]):
|
||||
if item.key == key:
|
||||
del self.table[hash_index][index]
|
||||
return
|
||||
raise KeyError('Key not found')
|
||||
141
docs/solutions/object_oriented_design/lru_cache/lru_cache.ipynb
Normal file
141
docs/solutions/object_oriented_design/lru_cache/lru_cache.ipynb
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design an LRU cache"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* What are we caching?\n",
|
||||
" * We are caching the results of web queries\n",
|
||||
"* Can we assume inputs are valid or do we have to validate them?\n",
|
||||
" * Assume they're valid\n",
|
||||
"* Can we assume this fits memory?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting lru_cache.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile lru_cache.py\n",
|
||||
"class Node(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, results):\n",
|
||||
" self.results = results\n",
|
||||
" self.prev = None\n",
|
||||
" self.next = None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class LinkedList(object):\n",
|
||||
"\n",
|
||||
" def __init__(self):\n",
|
||||
" self.head = None\n",
|
||||
" self.tail = None\n",
|
||||
"\n",
|
||||
" def move_to_front(self, node): # ...\n",
|
||||
" def append_to_front(self, node): # ...\n",
|
||||
" def remove_from_tail(self): # ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Cache(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, MAX_SIZE):\n",
|
||||
" self.MAX_SIZE = MAX_SIZE\n",
|
||||
" self.size = 0\n",
|
||||
" self.lookup = {} # key: query, value: node\n",
|
||||
" self.linked_list = LinkedList()\n",
|
||||
"\n",
|
||||
" def get(self, query)\n",
|
||||
" \"\"\"Get the stored query result from the cache.\n",
|
||||
" \n",
|
||||
" Accessing a node updates its position to the front of the LRU list.\n",
|
||||
" \"\"\"\n",
|
||||
" node = self.lookup.get(query)\n",
|
||||
" if node is None:\n",
|
||||
" return None\n",
|
||||
" self.linked_list.move_to_front(node)\n",
|
||||
" return node.results\n",
|
||||
"\n",
|
||||
" def set(self, results, query):\n",
|
||||
" \"\"\"Set the result for the given query key in the cache.\n",
|
||||
" \n",
|
||||
" When updating an entry, updates its position to the front of the LRU list.\n",
|
||||
" If the entry is new and the cache is at capacity, removes the oldest entry\n",
|
||||
" before the new entry is added.\n",
|
||||
" \"\"\"\n",
|
||||
" node = self.lookup.get(query)\n",
|
||||
" if node is not None:\n",
|
||||
" # Key exists in cache, update the value\n",
|
||||
" node.results = results\n",
|
||||
" self.linked_list.move_to_front(node)\n",
|
||||
" else:\n",
|
||||
" # Key does not exist in cache\n",
|
||||
" if self.size == self.MAX_SIZE:\n",
|
||||
" # Remove the oldest entry from the linked list and lookup\n",
|
||||
" self.lookup.pop(self.linked_list.tail.query, None)\n",
|
||||
" self.linked_list.remove_from_tail()\n",
|
||||
" else:\n",
|
||||
" self.size += 1\n",
|
||||
" # Add the new key and value\n",
|
||||
" new_node = Node(results)\n",
|
||||
" self.linked_list.append_to_front(new_node)\n",
|
||||
" self.lookup[query] = new_node"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.4.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
66
docs/solutions/object_oriented_design/lru_cache/lru_cache.py
Normal file
66
docs/solutions/object_oriented_design/lru_cache/lru_cache.py
Normal file
@@ -0,0 +1,66 @@
|
||||
class Node(object):
|
||||
|
||||
def __init__(self, results):
|
||||
self.results = results
|
||||
self.next = next
|
||||
|
||||
|
||||
class LinkedList(object):
|
||||
|
||||
def __init__(self):
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
def move_to_front(self, node):
|
||||
pass
|
||||
|
||||
def append_to_front(self, node):
|
||||
pass
|
||||
|
||||
def remove_from_tail(self):
|
||||
pass
|
||||
|
||||
|
||||
class Cache(object):
|
||||
|
||||
def __init__(self, MAX_SIZE):
|
||||
self.MAX_SIZE = MAX_SIZE
|
||||
self.size = 0
|
||||
self.lookup = {} # key: query, value: node
|
||||
self.linked_list = LinkedList()
|
||||
|
||||
def get(self, query):
|
||||
"""Get the stored query result from the cache.
|
||||
|
||||
Accessing a node updates its position to the front of the LRU list.
|
||||
"""
|
||||
node = self.lookup.get(query)
|
||||
if node is None:
|
||||
return None
|
||||
self.linked_list.move_to_front(node)
|
||||
return node.results
|
||||
|
||||
def set(self, results, query):
|
||||
"""Set the result for the given query key in the cache.
|
||||
|
||||
When updating an entry, updates its position to the front of the LRU list.
|
||||
If the entry is new and the cache is at capacity, removes the oldest entry
|
||||
before the new entry is added.
|
||||
"""
|
||||
node = self.lookup.get(query)
|
||||
if node is not None:
|
||||
# Key exists in cache, update the value
|
||||
node.results = results
|
||||
self.linked_list.move_to_front(node)
|
||||
else:
|
||||
# Key does not exist in cache
|
||||
if self.size == self.MAX_SIZE:
|
||||
# Remove the oldest entry from the linked list and lookup
|
||||
self.lookup.pop(self.linked_list.tail.query, None)
|
||||
self.linked_list.remove_from_tail()
|
||||
else:
|
||||
self.size += 1
|
||||
# Add the new key and value
|
||||
new_node = Node(results)
|
||||
self.linked_list.append_to_front(new_node)
|
||||
self.lookup[query] = new_node
|
||||
@@ -0,0 +1,159 @@
|
||||
package oopdesign;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Created by jeremyhu on 9/17/22.
|
||||
*/
|
||||
|
||||
|
||||
// Actors
|
||||
// Class Theater Kiosk
|
||||
// Handles the logic for buying a ticket and updating class theater info
|
||||
|
||||
// Class Theater:
|
||||
// Unique ID
|
||||
// Class Seats:
|
||||
// Two types: handicap, regular
|
||||
|
||||
// Class Movie
|
||||
// Name
|
||||
// date time
|
||||
// Theater ID
|
||||
// Cost: [regular, child, veteran, elderly]
|
||||
|
||||
// Workflow:
|
||||
// Buying a ticket and updating theater data
|
||||
// I will assume all the monetary transaction work is done elsewhere
|
||||
|
||||
public class MovieTickets {
|
||||
HashMap<Integer, Theater> theaters;
|
||||
private Integer theaterSizeDefault = 70;
|
||||
|
||||
enum TicketTypes {
|
||||
REGULAR, CHILD, VETERAN, ELDER;
|
||||
}
|
||||
|
||||
enum SeatTypes {
|
||||
HANDICAP, REGULAR;
|
||||
}
|
||||
|
||||
class Movie {
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private String title;
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
private LocalDate date;
|
||||
|
||||
public BigDecimal getRegularPrice() {
|
||||
return regularPrice;
|
||||
}
|
||||
|
||||
public void setRegularPrice(BigDecimal regularPrice) {
|
||||
this.regularPrice = regularPrice;
|
||||
}
|
||||
|
||||
private BigDecimal regularPrice;
|
||||
|
||||
public Theater getTheater() {
|
||||
return theater;
|
||||
}
|
||||
|
||||
public void setTheater(Theater theater) {
|
||||
this.theater = theater;
|
||||
}
|
||||
|
||||
private Theater theater;
|
||||
|
||||
public Movie(String title, LocalDate date, BigDecimal price, Theater theater) {
|
||||
this.title = title;
|
||||
this.date = date;
|
||||
this.regularPrice = price;
|
||||
this.theater = theater;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Kiosk {
|
||||
public int buyTicket(Movie m, Seat s, TicketTypes tType) {
|
||||
// I assume all the money transaction logic is taken care of somewhere else
|
||||
processTransaction(m, s, tType);
|
||||
|
||||
Theater theater = m.getTheater();
|
||||
|
||||
if(theater.remainingSeats() <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
theater.takeSeat(s.getId());
|
||||
return s.getId();
|
||||
}
|
||||
|
||||
public void processTransaction(Movie m, Seat s, TicketTypes tType) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class Seat {
|
||||
private int id;
|
||||
private SeatTypes type;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Seat(int id, SeatTypes type) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class Theater {
|
||||
private HashMap<Integer, Seat> seats;
|
||||
private int id;
|
||||
public Theater(int id) {
|
||||
this.id = id;
|
||||
this.seats = new HashMap<Integer, Seat>();
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setID(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int remainingSeats() {
|
||||
return seats.size();
|
||||
}
|
||||
|
||||
public int takeSeat(int seatID) {
|
||||
seats.remove(seatID);
|
||||
return seats.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Movie tickets kiosk system OOP example in Java
|
||||
- Jeremy Hu 09/2022
|
||||
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design an online chat"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* Assume we'll focus on the following workflows:\n",
|
||||
" * Text conversations only\n",
|
||||
" * Users\n",
|
||||
" * Add a user\n",
|
||||
" * Remove a user\n",
|
||||
" * Update a user\n",
|
||||
" * Add to a user's friends list\n",
|
||||
" * Add friend request\n",
|
||||
" * Approve friend request\n",
|
||||
" * Reject friend request\n",
|
||||
" * Remove from a user's friends list\n",
|
||||
" * Create a group chat\n",
|
||||
" * Invite friends to a group chat\n",
|
||||
" * Post a message to a group chat\n",
|
||||
" * Private 1-1 chat\n",
|
||||
" * Invite a friend to a private chat\n",
|
||||
" * Post a message to a private chat\n",
|
||||
"* No need to worry about scaling initially"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting online_chat.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile online_chat.py\n",
|
||||
"from abc import ABCMeta\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class UserService(object):\n",
|
||||
"\n",
|
||||
" def __init__(self):\n",
|
||||
" self.users_by_id = {} # key: user id, value: User\n",
|
||||
"\n",
|
||||
" def add_user(self, user_id, name, pass_hash): # ...\n",
|
||||
" def remove_user(self, user_id): # ...\n",
|
||||
" def add_friend_request(self, from_user_id, to_user_id): # ...\n",
|
||||
" def approve_friend_request(self, from_user_id, to_user_id): # ...\n",
|
||||
" def reject_friend_request(self, from_user_id, to_user_id): # ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class User(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, user_id, name, pass_hash):\n",
|
||||
" self.user_id = user_id\n",
|
||||
" self.name = name\n",
|
||||
" self.pass_hash = pass_hash\n",
|
||||
" self.friends_by_id = {} # key: friend id, value: User\n",
|
||||
" self.friend_ids_to_private_chats = {} # key: friend id, value: private chats\n",
|
||||
" self.group_chats_by_id = {} # key: chat id, value: GroupChat\n",
|
||||
" self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n",
|
||||
" self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n",
|
||||
"\n",
|
||||
" def message_user(self, friend_id, message): # ...\n",
|
||||
" def message_group(self, group_id, message): # ...\n",
|
||||
" def send_friend_request(self, friend_id): # ...\n",
|
||||
" def receive_friend_request(self, friend_id): # ...\n",
|
||||
" def approve_friend_request(self, friend_id): # ...\n",
|
||||
" def reject_friend_request(self, friend_id): # ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Chat(metaclass=ABCMeta):\n",
|
||||
"\n",
|
||||
" def __init__(self, chat_id):\n",
|
||||
" self.chat_id = chat_id\n",
|
||||
" self.users = []\n",
|
||||
" self.messages = []\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class PrivateChat(Chat):\n",
|
||||
"\n",
|
||||
" def __init__(self, first_user, second_user):\n",
|
||||
" super(PrivateChat, self).__init__()\n",
|
||||
" self.users.append(first_user)\n",
|
||||
" self.users.append(second_user)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class GroupChat(Chat):\n",
|
||||
"\n",
|
||||
" def add_user(self, user): # ...\n",
|
||||
" def remove_user(self, user): # ... \n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Message(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, message_id, message, timestamp):\n",
|
||||
" self.message_id = message_id\n",
|
||||
" self.message = message\n",
|
||||
" self.timestamp = timestamp\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class AddRequest(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, from_user_id, to_user_id, request_status, timestamp):\n",
|
||||
" self.from_user_id = from_user_id\n",
|
||||
" self.to_user_id = to_user_id\n",
|
||||
" self.request_status = request_status\n",
|
||||
" self.timestamp = timestamp\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class RequestStatus(Enum):\n",
|
||||
"\n",
|
||||
" UNREAD = 0\n",
|
||||
" READ = 1\n",
|
||||
" ACCEPTED = 2\n",
|
||||
" REJECTED = 3"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
110
docs/solutions/object_oriented_design/online_chat/online_chat.py
Normal file
110
docs/solutions/object_oriented_design/online_chat/online_chat.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from abc import ABCMeta
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class UserService(object):
|
||||
|
||||
def __init__(self):
|
||||
self.users_by_id = {} # key: user id, value: User
|
||||
|
||||
def add_user(self, user_id, name, pass_hash):
|
||||
if user_id not in self.users_by_id:
|
||||
new_user = User(user_id, name, pass_hash)
|
||||
self.users_by_id[user_id] = new_user
|
||||
return new_user
|
||||
else:
|
||||
# User with this ID already exists
|
||||
return None
|
||||
|
||||
def remove_user(self, user_id):
|
||||
pass
|
||||
|
||||
def add_friend_request(self, from_user_id, to_user_id):
|
||||
pass
|
||||
|
||||
def approve_friend_request(self, from_user_id, to_user_id):
|
||||
pass
|
||||
|
||||
def reject_friend_request(self, from_user_id, to_user_id):
|
||||
pass
|
||||
|
||||
|
||||
class User(object):
|
||||
|
||||
def __init__(self, user_id, name, pass_hash):
|
||||
self.user_id = user_id
|
||||
self.name = name
|
||||
self.pass_hash = pass_hash
|
||||
self.friends_by_id = {} # key: friend id, value: User
|
||||
self.friend_ids_to_private_chats = {} # key: friend id, value: private chats
|
||||
self.group_chats_by_id = {} # key: chat id, value: GroupChat
|
||||
self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
|
||||
self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
|
||||
|
||||
def message_user(self, friend_id, message):
|
||||
pass
|
||||
|
||||
def message_group(self, group_id, message):
|
||||
pass
|
||||
|
||||
def send_friend_request(self, friend_id):
|
||||
pass
|
||||
|
||||
def receive_friend_request(self, friend_id):
|
||||
pass
|
||||
|
||||
def approve_friend_request(self, friend_id):
|
||||
pass
|
||||
|
||||
def reject_friend_request(self, friend_id):
|
||||
pass
|
||||
|
||||
|
||||
class Chat(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, chat_id):
|
||||
self.chat_id = chat_id
|
||||
self.users = []
|
||||
self.messages = []
|
||||
|
||||
|
||||
class PrivateChat(Chat):
|
||||
|
||||
def __init__(self, first_user, second_user):
|
||||
super(PrivateChat, self).__init__()
|
||||
self.users.append(first_user)
|
||||
self.users.append(second_user)
|
||||
|
||||
|
||||
class GroupChat(Chat):
|
||||
|
||||
def add_user(self, user):
|
||||
pass
|
||||
|
||||
def remove_user(self, user):
|
||||
pass
|
||||
|
||||
|
||||
class Message(object):
|
||||
|
||||
def __init__(self, message_id, message, timestamp):
|
||||
self.message_id = message_id
|
||||
self.message = message
|
||||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class AddRequest(object):
|
||||
|
||||
def __init__(self, from_user_id, to_user_id, request_status, timestamp):
|
||||
self.from_user_id = from_user_id
|
||||
self.to_user_id = to_user_id
|
||||
self.request_status = request_status
|
||||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class RequestStatus(Enum):
|
||||
|
||||
UNREAD = 0
|
||||
READ = 1
|
||||
ACCEPTED = 2
|
||||
REJECTED = 3
|
||||
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/ido777/system-design-primer-update)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Design a parking lot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Constraints and assumptions\n",
|
||||
"\n",
|
||||
"* What types of vehicles should we support?\n",
|
||||
" * Motorcycle, Car, Bus\n",
|
||||
"* Does each vehicle type take up a different amount of parking spots?\n",
|
||||
" * Yes\n",
|
||||
" * Motorcycle spot -> Motorcycle\n",
|
||||
" * Compact spot -> Motorcycle, Car\n",
|
||||
" * Large spot -> Motorcycle, Car\n",
|
||||
" * Bus can park if we have 5 consecutive \"large\" spots\n",
|
||||
"* Does the parking lot have multiple levels?\n",
|
||||
" * Yes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Solution"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Overwriting parking_lot.py\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%%writefile parking_lot.py\n",
|
||||
"from abc import ABCMeta, abstractmethod\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class VehicleSize(Enum):\n",
|
||||
"\n",
|
||||
" MOTORCYCLE = 0\n",
|
||||
" COMPACT = 1\n",
|
||||
" LARGE = 2\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Vehicle(metaclass=ABCMeta):\n",
|
||||
"\n",
|
||||
" def __init__(self, vehicle_size, license_plate, spot_size):\n",
|
||||
" self.vehicle_size = vehicle_size\n",
|
||||
" self.license_plate = license_plate\n",
|
||||
" self.spot_size = spot_size\n",
|
||||
" self.spots_taken = []\n",
|
||||
"\n",
|
||||
" def clear_spots(self):\n",
|
||||
" for spot in self.spots_taken:\n",
|
||||
" spot.remove_vehicle(self)\n",
|
||||
" self.spots_taken = []\n",
|
||||
"\n",
|
||||
" def take_spot(self, spot):\n",
|
||||
" self.spots_taken.append(spot)\n",
|
||||
"\n",
|
||||
" @abstractmethod\n",
|
||||
" def can_fit_in_spot(self, spot):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Motorcycle(Vehicle):\n",
|
||||
"\n",
|
||||
" def __init__(self, license_plate):\n",
|
||||
" super(Motorcycle, self).__init__(VehicleSize.MOTORCYCLE, license_plate, spot_size=1)\n",
|
||||
"\n",
|
||||
" def can_fit_in_spot(self, spot):\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Car(Vehicle):\n",
|
||||
"\n",
|
||||
" def __init__(self, license_plate):\n",
|
||||
" super(Car, self).__init__(VehicleSize.COMPACT, license_plate, spot_size=1)\n",
|
||||
"\n",
|
||||
" def can_fit_in_spot(self, spot):\n",
|
||||
" return True if (spot.size == LARGE or spot.size == COMPACT) else False\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Bus(Vehicle):\n",
|
||||
"\n",
|
||||
" def __init__(self, license_plate):\n",
|
||||
" super(Bus, self).__init__(VehicleSize.LARGE, license_plate, spot_size=5)\n",
|
||||
"\n",
|
||||
" def can_fit_in_spot(self, spot):\n",
|
||||
" return True if spot.size == LARGE else False\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class ParkingLot(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, num_levels):\n",
|
||||
" self.num_levels = num_levels\n",
|
||||
" self.levels = []\n",
|
||||
"\n",
|
||||
" def park_vehicle(self, vehicle):\n",
|
||||
" for level in levels:\n",
|
||||
" if level.park_vehicle(vehicle):\n",
|
||||
" return True\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Level(object):\n",
|
||||
"\n",
|
||||
" SPOTS_PER_ROW = 10\n",
|
||||
"\n",
|
||||
" def __init__(self, floor, total_spots):\n",
|
||||
" self.floor = floor\n",
|
||||
" self.num_spots = total_spots\n",
|
||||
" self.available_spots = 0\n",
|
||||
" self.parking_spots = []\n",
|
||||
"\n",
|
||||
" def spot_freed(self):\n",
|
||||
" self.available_spots += 1\n",
|
||||
"\n",
|
||||
" def park_vehicle(self, vehicle):\n",
|
||||
" spot = self._find_available_spot(vehicle)\n",
|
||||
" if spot is None:\n",
|
||||
" return None\n",
|
||||
" else:\n",
|
||||
" spot.park_vehicle(vehicle)\n",
|
||||
" return spot\n",
|
||||
"\n",
|
||||
" def _find_available_spot(self, vehicle):\n",
|
||||
" \"\"\"Find an available spot where vehicle can fit, or return None\"\"\"\n",
|
||||
" # ...\n",
|
||||
"\n",
|
||||
" def _park_starting_at_spot(self, spot, vehicle):\n",
|
||||
" \"\"\"Occupy starting at spot.spot_number to vehicle.spot_size.\"\"\"\n",
|
||||
" # ...\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class ParkingSpot(object):\n",
|
||||
"\n",
|
||||
" def __init__(self, level, row, spot_number, spot_size, vehicle_size):\n",
|
||||
" self.level = level\n",
|
||||
" self.row = row\n",
|
||||
" self.spot_number = spot_number\n",
|
||||
" self.spot_size = spot_size\n",
|
||||
" self.vehicle_size = vehicle_size\n",
|
||||
" self.vehicle = None\n",
|
||||
"\n",
|
||||
" def is_available(self):\n",
|
||||
" return True if self.vehicle is None else False\n",
|
||||
"\n",
|
||||
" def can_fit_vehicle(self, vehicle):\n",
|
||||
" if self.vehicle is not None:\n",
|
||||
" return False\n",
|
||||
" return vehicle.can_fit_in_spot(self)\n",
|
||||
"\n",
|
||||
" def park_vehicle(self, vehicle): # ...\n",
|
||||
" def remove_vehicle(self): # ..."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.4.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
125
docs/solutions/object_oriented_design/parking_lot/parking_lot.py
Normal file
125
docs/solutions/object_oriented_design/parking_lot/parking_lot.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class VehicleSize(Enum):
|
||||
|
||||
MOTORCYCLE = 0
|
||||
COMPACT = 1
|
||||
LARGE = 2
|
||||
|
||||
|
||||
class Vehicle(metaclass=ABCMeta):
|
||||
|
||||
def __init__(self, vehicle_size, license_plate, spot_size):
|
||||
self.vehicle_size = vehicle_size
|
||||
self.license_plate = license_plate
|
||||
self.spot_size = spot_size
|
||||
self.spots_taken = []
|
||||
|
||||
def clear_spots(self):
|
||||
for spot in self.spots_taken:
|
||||
spot.remove_vehicle(self)
|
||||
self.spots_taken = []
|
||||
|
||||
def take_spot(self, spot):
|
||||
self.spots_taken.append(spot)
|
||||
|
||||
@abstractmethod
|
||||
def can_fit_in_spot(self, spot):
|
||||
pass
|
||||
|
||||
|
||||
class Motorcycle(Vehicle):
|
||||
|
||||
def __init__(self, license_plate):
|
||||
super().__init__(VehicleSize.MOTORCYCLE, license_plate, spot_size=1)
|
||||
|
||||
def can_fit_in_spot(self, spot):
|
||||
return True
|
||||
|
||||
|
||||
class Car(Vehicle):
|
||||
|
||||
def __init__(self, license_plate):
|
||||
super().__init__(VehicleSize.COMPACT, license_plate, spot_size=1)
|
||||
|
||||
def can_fit_in_spot(self, spot):
|
||||
return spot.size in (VehicleSize.LARGE, VehicleSize.COMPACT)
|
||||
|
||||
|
||||
class Bus(Vehicle):
|
||||
|
||||
def __init__(self, license_plate):
|
||||
super().__init__(VehicleSize.LARGE, license_plate, spot_size=5)
|
||||
|
||||
def can_fit_in_spot(self, spot):
|
||||
return spot.size == VehicleSize.LARGE
|
||||
|
||||
|
||||
class ParkingLot(object):
|
||||
|
||||
def __init__(self, num_levels):
|
||||
self.num_levels = num_levels
|
||||
self.levels = [] # List of Levels
|
||||
|
||||
def park_vehicle(self, vehicle):
|
||||
for level in self.levels:
|
||||
if level.park_vehicle(vehicle):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Level(object):
|
||||
|
||||
SPOTS_PER_ROW = 10
|
||||
|
||||
def __init__(self, floor, total_spots):
|
||||
self.floor = floor
|
||||
self.num_spots = total_spots
|
||||
self.available_spots = 0
|
||||
self.spots = [] # List of ParkingSpots
|
||||
|
||||
def spot_freed(self):
|
||||
self.available_spots += 1
|
||||
|
||||
def park_vehicle(self, vehicle):
|
||||
spot = self._find_available_spot(vehicle)
|
||||
if spot is None:
|
||||
return None
|
||||
else:
|
||||
spot.park_vehicle(vehicle)
|
||||
return spot
|
||||
|
||||
def _find_available_spot(self, vehicle):
|
||||
"""Find an available spot where vehicle can fit, or return None"""
|
||||
pass
|
||||
|
||||
def _park_starting_at_spot(self, spot, vehicle):
|
||||
"""Occupy starting at spot.spot_number to vehicle.spot_size."""
|
||||
pass
|
||||
|
||||
|
||||
class ParkingSpot(object):
|
||||
|
||||
def __init__(self, level, row, spot_number, spot_size, vehicle_size):
|
||||
self.level = level
|
||||
self.row = row
|
||||
self.spot_number = spot_number
|
||||
self.spot_size = spot_size
|
||||
self.vehicle_size = vehicle_size
|
||||
self.vehicle = None
|
||||
|
||||
def is_available(self):
|
||||
return self.vehicle == None
|
||||
|
||||
def can_fit_vehicle(self, vehicle):
|
||||
if self.vehicle is not None:
|
||||
return False
|
||||
return vehicle.can_fit_in_spot(self)
|
||||
|
||||
def park_vehicle(self, vehicle):
|
||||
pass
|
||||
|
||||
def remove_vehicle(self):
|
||||
pass
|
||||
Reference in New Issue
Block a user