This commit is contained in:
Hykilpikonna
2021-12-07 22:28:01 -05:00
commit 6fffdf686a
151 changed files with 37985 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>10.1 The Problem Domain: Food Delivery Networks</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">10.1 The Problem Domain: Food Delivery Networks</h1>
</header>
<section>
<p>We do not write software in a vacuum; we study computer science to learn how to use vast computational power to solve real-world problems. As professionals in industry and academia, the programs we create serve a purpose, whether to satisfy a need from a client, to improve individual lives and society, or to advance human knowledge and technology. In the previous chapters of these course notes, we have learned about the fundamentals of programming, mathematical proof, and algorithm analysis. We have focused on developed the knowledge and skills required to create and analyse programs.</p>
<p>In this final chapter of the course, we will take what weve learned and apply it to design and implement a large program to solve a real-world problem. As a first step to this, well learn about how to approach a new domain to understand how we can apply computer science techniques to both represent and solve problems in that domain.</p>
<h2 id="what-is-a-problem-domain">What is a problem domain?</h2>
<p>A <strong>problem domain</strong> is collection of knowledge<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> We use the term <em>domain-specific</em> knowledge to refer to knowledge about a particular domain. Society often uses the term <em>domain experts</em> to refer to people who have a great deal of knowledge in a particular domain.</span> about a specific field, phenomenon, or discipline, and an understanding of the goals, problems, deficiencies, and/or desired improvements within that area. Each problem domains encompass many different kinds of knowledge, including terminology and definitions, concepts and skills, and context and history. Through your lectures, tutorials, and assignments, youve touched on a wide array of problem domains, such as tracking marriage records in the City of Toronto, modelling the spread of infectious diseases, generating course timetables as U of T students, and cryptography.</p>
<p>Lets unpack how we explored the domain of cryptography in Chapter 7. We first introduced the key scenario of two people communicating securely so that their messages could not be deciphered by an eavesdropper. As we dove into cryptography, we learned about:</p>
<ul>
<li>terminology and definitions (e.g., symmetric-key and public-key cryptosystems, encryption and decryption, various existing cryptosystems)</li>
<li>concepts and skills (e.g., proving that a cryptosystem is correct; justifying the security of a cryptosystem based on the presumed hardness of mathematical problems like Integer Factorization)</li>
<li>the context and history (e.g., ancient cryptosystems, how cryptography is applied to Internet communciations)</li>
</ul>
<p>Our previous study of programming enabled us to write programs, but we had to learn all about the domain of cryptography before being able to implement cryptographic algorithms ourselves. Our knowledge of Python programming alone might have been sufficient to explain what operations are performed on what data in, for example, <code>rsa_generate_key</code>, <code>rsa_encrypt</code>, and <code>rsa_decrypt</code>. But it was the domain-specific knowledge we learned that explained <em>how</em> we came up with these algorithms and why they are correct.</p>
<h2 id="introducing-hercules">Introducing Hercules</h2>
<p>Now, well introduce a new problem domain that we will spend the rest of this chapter studying.</p>
<p>Consider a person or household self-quarantining during the pandemic. One of the main logistical challenges they have to face is how to arrange for food during their quarantine. To help address this need, you have founded <em>Hercules Ltd.</em>, a non-profit organization that allows people under quarantine to order groceries and meals from grocery stores and restaurants, and arranges for couriers to make deliveries right to their front doors. You are incredibly excited and cant wait to launch a Hercules app. Your friend is a bit more cautious, and wonders how many couriers will be needed to make grocery and meal deliveries in a timely manner, which of course will depend on how many people use the app. You and your friend decide to put the computational skills youve learned in this course to help answer this question.</p>
<p>This problem domain is likely a familiar one; the idea of having food delivered to your doorstep has existed for a long time. The preceding paragraph uses some familiar terminology, such as couriers and deliveries. You may even be familiar with existing apps that already do this, such as <em>UberEats</em>, <em>Skip the Dishes</em>, or <em>Instacart</em>. When thinking about designing and implementing this app, you are probably considering:</p>
<ul>
<li>how restaurants will register with the app and post menus</li>
<li>how customers will register with the app to browse restaurants and place orders</li>
<li>how couriers will register with the app to claim orders and deliver them from restaurants to customers</li>
<li>…and more</li>
</ul>
<h3 id="food-delivery-as-a-system">Food delivery as a system</h3>
<p>We can view food delivery in Toronto as a <strong>system</strong>, which is a group of entities (or agents) that interact with each other over time. Systems modeling is frequently used to conceptualize how an organization operates. The first part of creating a computational model for such a system is to design and implement the various entities in the system—in the case of the Hercules Ltd., these are entities like couriers and the customers placing orders.</p>
<p>The entities in a system are not static; they change over time. New people sign up and place food orders; couriers pick up meals from restaurants and deliver them to clients. For a live app, these events are driven by real humans interacting with the app in real-time. In this chapter, however, were going to look at another way of driving change in our food delivery system over time. The second part of our computational model is a <em>simulation</em> that uses randomness to generate events that cause the system to change over time. For example, our food delivery simulation will specify how often customers place an order, taking into account that some times of day are busier than others.</p>
<p>Computational simulations are a powerful tool because they harness the speed and reliability of your computer to perform complex calculations and produce results that can be analysed and visualized. But simulations are reliant on the accuracy of their underlying mathematical models, and are ultimately approximations of the real world. A well-designed simulation allows the programmer to start with a simple model and extend and tweak it in response to new domain-specific knowledge.</p>
<h3 id="chapter-roadmap">Chapter roadmap</h3>
<p>Over the course of this chapter, well study how to design and implement both of these parts of a computational model for our food delivery platform, Hercules. This case study will also give us an opportunity to explore the design of a relatively complex software system. Well use what weve learned about classes to model the entities in a food delivery network, and study a specific kind of simulation known as the <em>discrete-event simulation</em>. We hope youre excited. Hercules is counting on you!</p>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+246
View File
@@ -0,0 +1,246 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>10.2 Object-Oriented Modelling of Our Problem Domain</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">10.2 Object-Oriented Modelling of Our Problem Domain</h1>
</header>
<section>
<p>In the previous section, we said that a system is a collection of entities that interact with each other over time. In this section, we will explore what data should be a part of our problem domain—a food delivery system—and how that data might change over time. Well introduce an object-oriented approach to modelling this data in Python, using both data classes and general classes to represent different entities.</p>
<p>One thing to keep in mind as we proceed through this section (and the rest of the chapter) is that just like in the “real world”, the scope of our problem domain is not fixed and can change over time. We are interested in the minimum set of data needed for our system to be meaningful, keeping the scope small at first with the potential to expand over time. Throughout this section, well point out places where we make <em>simplifying assumptions</em> that reduce the complexity of our system, which can serve as potential avenues for your own independent explorations after working through this chapter.</p>
<h2 id="entities-in-a-food-delivery-system">Entities in a food delivery system</h2>
<p>A good first step in modelling our problem domain is to identify the relevant entities in the domain. Here is our initial description of <em>Hercules</em> from the previous section:</p>
<blockquote>
<p>Consider a person or household doing a self-quarantine during the pandemic. One of the main logistical challenges they have to face is how to arrange for food during their quarantine. To help address this need, you have founded <em>Hercules Ltd.</em>, a non-profit organization that allows people under quarantine to order groceries and meals from grocery stores and restaurants, and arranges for couriers to make deliveries right to their front doors.</p>
</blockquote>
<p>We use two strategies for picking out relevant entities from an English description like this one:</p>
<ol type="1">
<li>Identify different roles that people/groups play in the domain. Each “role” is likely an entity: e.g., <em>customer</em>, <em>courier</em>, and <em>restaurant</em> are three distinct roles in the system.</li>
<li>Identify a bundle of data that makes sense as a logical unit. Each “bundle” is likely an entity: e.g., an <em>order</em> is a bundle of related information about a users food request.</li>
</ol>
<p>In an object-oriented design, a standard approach is to create a class to represent each of these entities. Should we make a data class or a general class for each one? There are no easy answers to this question, but a good strategy to use is to <em>start</em> with a data class, since data classes are easier to create, and turn it into a general class if we need a more complex design (e.g., to add methods, including the initializer, or mark attributes as private).</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="at">@dataclass</span></span>
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw">class</span> Restaurant:</span>
<span id="cb1-3"><a href="#cb1-3"></a> <span class="co">&quot;&quot;&quot;A place that serves food.&quot;&quot;&quot;</span></span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="at">@dataclass</span></span>
<span id="cb1-6"><a href="#cb1-6"></a><span class="kw">class</span> Customer:</span>
<span id="cb1-7"><a href="#cb1-7"></a> <span class="co">&quot;&quot;&quot;A person who orders food.&quot;&quot;&quot;</span></span>
<span id="cb1-8"><a href="#cb1-8"></a></span>
<span id="cb1-9"><a href="#cb1-9"></a><span class="at">@dataclass</span></span>
<span id="cb1-10"><a href="#cb1-10"></a><span class="kw">class</span> Courier:</span>
<span id="cb1-11"><a href="#cb1-11"></a> <span class="co">&quot;&quot;&quot;A person who delivers food orders from restaurants to customers.&quot;&quot;&quot;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a></span>
<span id="cb1-13"><a href="#cb1-13"></a><span class="at">@dataclass</span></span>
<span id="cb1-14"><a href="#cb1-14"></a><span class="kw">class</span> Order:</span>
<span id="cb1-15"><a href="#cb1-15"></a> <span class="co">&quot;&quot;&quot;A food order from a customer.&quot;&quot;&quot;</span></span></code></pre></div>
<p>Once we have identified the classes representing the entities in the system, we now dive into the details of the system to identify appropriate <em>attributes</em> for each of these data classes. Well discuss our process for two of these data classes in this section, and leave the other two to lecture this week.</p>
<h2 id="designing-the-restaurant-data-class">Designing the <code>Restaurant</code> data class</h2>
<p>Let us consider how we might design a restaurant data class. What would a restaurant need to have stored as data? It is useful to envision how a user might interact with the app. A user might want to browse a list of restaurants available, and so we need a way to identify each restaurant: its <em>name</em>. After selecting a restaurant, a user needs to see what food is available to order, so we need to store a <em>food menu</em> for each restaurant. Finally, couriers need to know where restaurants are in order to pick up food orders, and so we need to store a <em>location</em> for each restaurant.</p>
<p>Each of these three pieces of information—restaurant name, food menu, and location—are appropriate <em>attributes</em> for the restaurant. Now we have to decide what data types to use to represent this data. You have much practice doing this, stretching back to all the way to the beginning of this course! Yet as well see, there are design decisions to be made even when choosing individual attributes.</p>
<ul>
<li><p>The restaurant <em>name</em> is fairly straightforward: well use a <code>str</code> to represent it.</p></li>
<li><p>The restaurant <em>menu</em> has a few different options. For this section, well use a <code>dict</code> that maps the names of dishes (<code>str</code>s) to their price (<code>float</code>s).</p></li>
<li><p>There are many ways to represent a restaurants location. For example, we could store its address, as a <code>str</code>. Or we could improve the precision of our data and store the latitude and longitude (a tuple of <code>float</code>s), which would be useful for displaying restaurants on maps.</p>
<p>For now, well store both address and latitude/longitude information for each restaurant. It may be that both representations are useful, and should be stored by our application.</p></li>
</ul>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="at">@dataclass</span></span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="kw">class</span> Restaurant:</span>
<span id="cb2-3"><a href="#cb2-3"></a> <span class="co">&quot;&quot;&quot;A place that serves food.</span></span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb2-6"><a href="#cb2-6"></a><span class="co"> - name: the name of the restaurant</span></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> - address: the address of the restaurant</span></span>
<span id="cb2-8"><a href="#cb2-8"></a><span class="co"> - menu: the menu of the restaurant with the name of the dish mapping to</span></span>
<span id="cb2-9"><a href="#cb2-9"></a><span class="co"> the price</span></span>
<span id="cb2-10"><a href="#cb2-10"></a><span class="co"> - location: the location of the restaurant as (latitude, longitude)</span></span>
<span id="cb2-11"><a href="#cb2-11"></a></span>
<span id="cb2-12"><a href="#cb2-12"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb2-13"><a href="#cb2-13"></a><span class="co"> - all(self.menu[item] &gt;= 0 for item in self.menu)</span></span>
<span id="cb2-14"><a href="#cb2-14"></a><span class="co"> - -90 &lt;= self.location[0] &lt;= 90</span></span>
<span id="cb2-15"><a href="#cb2-15"></a><span class="co"> - -180 &lt;= self.location[1] &lt;= 180</span></span>
<span id="cb2-16"><a href="#cb2-16"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-17"><a href="#cb2-17"></a> name: <span class="bu">str</span></span>
<span id="cb2-18"><a href="#cb2-18"></a> address: <span class="bu">str</span></span>
<span id="cb2-19"><a href="#cb2-19"></a> menu: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">float</span>]</span>
<span id="cb2-20"><a href="#cb2-20"></a> location: <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">float</span>]</span></code></pre></div>
<p>There is one other subtlety with this design before we move on. The menu is a compound data type, and we chose to represent it using one of Pythons built-in data structures. But another approach would have been to create a completely separate <code>Menu</code> data class. That is certainly a viable option, but we were wary of falling into the trap of creating too many classes in our simulation. Each new class we create introduces a little more complexity into our program, and for a relatively simple class for a menu, we did not think this additional complexity was worth it.</p>
<p>On the flip side, we could have used a dictionary to represent a restaurant instead of a <code>Restaurant</code> data class. This would have reduced on area of complexity (the number of classes to keep track of), but introduced another (the “valid” keys of a dictionary used to represent a restaurant). There is always a trade-off in design, and when evaluating trade-offs one should never forget cognitive load on the programmer.</p>
<h2 id="designing-the-order-data-class">Designing the <code>Order</code> data class</h2>
<p>Now lets discuss a data class thats a bit more abstract: a single order. An order must track the <em>customer</em> who placed the order, the <em>restaurant</em> where the food is being ordered from, and the <em>food items</em> that are being ordered. We can also imagine that an order should have an associated courier who has been assigned to deliver the order. Well also keep track of when the order was created, and when the order is completed.</p>
<p>Theres one subtlety with two of these attributes: the associated courier and the time when the order is completed might only be assigned values after the order has been created. So we use a default value <code>None</code> to assign to these two instance attributes when an <code>Order</code> is first created. We could implement this by converting the data class to a general class and writing our own <code>__init__</code> method, but instead well take advantage of a new feature with data classes: the ability to specify default values for an instance attribute after the type annotation.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="at">@dataclass</span></span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="kw">class</span> Order:</span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="co">&quot;&quot;&quot;A food order from a customer.</span></span>
<span id="cb3-4"><a href="#cb3-4"></a></span>
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> Attributes:</span></span>
<span id="cb3-6"><a href="#cb3-6"></a><span class="co"> - customer: the name of the customer who placed this order</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="co"> - restaurant: the name of the restaurant the order is placed for</span></span>
<span id="cb3-8"><a href="#cb3-8"></a><span class="co"> - food_items: a mapping from names of food to the quantity being ordered</span></span>
<span id="cb3-9"><a href="#cb3-9"></a><span class="co"> - start_time: the time the order was placed</span></span>
<span id="cb3-10"><a href="#cb3-10"></a><span class="co"> - courier: the courier assigned to this order (initially None)</span></span>
<span id="cb3-11"><a href="#cb3-11"></a><span class="co"> - end_time: the time the order was completed by the courier (initially None)</span></span>
<span id="cb3-12"><a href="#cb3-12"></a></span>
<span id="cb3-13"><a href="#cb3-13"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb3-14"><a href="#cb3-14"></a><span class="co"> - self.food_items != []</span></span>
<span id="cb3-15"><a href="#cb3-15"></a><span class="co"> - all(self.food_items[i][1] &gt; 0 for i in range(len(self.food_items)))</span></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-17"><a href="#cb3-17"></a> customer: Customer</span>
<span id="cb3-18"><a href="#cb3-18"></a> restaurant: Restaurant</span>
<span id="cb3-19"><a href="#cb3-19"></a> food_items: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">int</span>]</span>
<span id="cb3-20"><a href="#cb3-20"></a> start_time: datetime.datetime</span>
<span id="cb3-21"><a href="#cb3-21"></a> courier: Optional[Courier] <span class="op">=</span> <span class="va">None</span></span>
<span id="cb3-22"><a href="#cb3-22"></a> end_time: Optional[datetime.datetime] <span class="op">=</span> <span class="va">None</span></span></code></pre></div>
<p>The line <code>courier: Optional[Courier] = None</code> is how we define an instance attribute <code>Courier</code> with a default value of <code>None</code>. The type annotation <code>Optional[Courier]</code> means that this attribute can either be <code>None</code> or a <code>Courier</code> instance. Similarly, the <code>end_time</code> attribute must be either <code>None</code> (its initial value) or a <code>datetime.datetime</code> value.</p>
<p>Here is how we could use this class (note that <code>Customer</code> is currently an empty data class, and so is instantiated simply as <code>Customer()</code>):</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="op">&gt;&gt;&gt;</span> david <span class="op">=</span> Customer()</span>
<span id="cb4-2"><a href="#cb4-2"></a><span class="op">&gt;&gt;&gt;</span> mcdonalds <span class="op">=</span> Restaurant(name<span class="op">=</span><span class="st">&#39;McDonalds&#39;</span>, address<span class="op">=</span><span class="st">&#39;160 Spadina Ave&#39;</span>,</span>
<span id="cb4-3"><a href="#cb4-3"></a>... menu<span class="op">=</span>{<span class="st">&#39;fries&#39;</span>: <span class="fl">4.5</span>}, location<span class="op">=</span>(<span class="fl">43.649</span>, <span class="op">-</span><span class="fl">79.397</span>))</span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="op">&gt;&gt;&gt;</span> order <span class="op">=</span> Order(customer<span class="op">=</span>david, restaurant<span class="op">=</span>mcdonalds,</span>
<span id="cb4-5"><a href="#cb4-5"></a>... food_items<span class="op">=</span>{<span class="st">&#39;fries&#39;</span>: <span class="dv">10</span>},</span>
<span id="cb4-6"><a href="#cb4-6"></a>... start_time<span class="op">=</span>datetime.datetime(<span class="dv">2020</span>, <span class="dv">11</span>, <span class="dv">5</span>, <span class="dv">11</span>, <span class="dv">30</span>))</span>
<span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="op">&gt;&gt;&gt;</span> order.courier <span class="kw">is</span> <span class="va">None</span> <span class="co"># Illustrating default values</span></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="va">True</span></span>
<span id="cb4-10"><a href="#cb4-10"></a><span class="op">&gt;&gt;&gt;</span> order.end_time <span class="kw">is</span> <span class="va">None</span></span>
<span id="cb4-11"><a href="#cb4-11"></a><span class="va">True</span></span></code></pre></div>
<h2 id="class-composition">Class composition</h2>
<p>Just as we saw earlier in the course that built-in collection types like lists can be nested within each other, classes can also be “nested” within each other through their instance attributes. Our above <code>Order</code> data class has attributes which are instances of other classes we have defined (<code>Customer</code>, <code>Restaurant</code>, and <code>Courier</code>).</p>
<p>The relationship between <code>Order</code> and these other classes is called <strong>class composition</strong>, and is a fundamental to object-oriented design. When we create classes for a computational model, these classes dont exist in isolation. They can interact with each other in several ways, one of which is composition. We use class composition to represent a “has a” relationship between two classes (we say that “an <code>Order</code> has a <code>Customer</code>”).<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> This is in contrast to inheritance, which defines an “is a” relationships between two classes, e.g. “<code>Stack1</code> is a <code>Stack</code>”.</span></p>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+254
View File
@@ -0,0 +1,254 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>10.3 A “Manager” Class</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">10.3 A “Manager” Class</h1>
</header>
<section>
<p>In the previous section, we defined four different data classes—<code>Restaurant</code>, <code>Customer</code>, <code>Courier</code>, <code>Order</code>—to represent different entities in our food delivery system. We must now determine how to keep track of all of these entities, and how they can interact with each other. For example, as a user I would want to be able to look up a list of restaurants in my area to order food from. In code, how does a single <code>Customer</code> object “know” about all the different <code>Restaurant</code>s in the system? Should each <code>Customer</code> have an attribute containing list of <code>Restaurant</code>s?<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> The question of how objects “know” about other objects is similar to the notion of variable <em>scope</em>. A variables scope determines where it can be accessed in a program; the scope of an object dictates the objects lifetime and who the object belongs to. But now consider our current problem domain, with the hundreds of restaurants and potential thousands of customers. What should the scope of all those objects be?</span></p>
<p>There are many ways to approach this problem. A common object-oriented design approach is to create a new manager class whose role is to keep track of all of the entities in the system and to mediate the interactions between them (like a customer placing a new order). This class is more complex than the others we saw in the last section, and so we will not use a data class, and instead use a general class with a custom initializer and keep most of the instance attributes private.</p>
<p>Here is the manager class well create for our food delivery system. The <code>FoodDeliverySystem</code> class will store (and have access to) every customer, courier, and restaurant represented in our system.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">class</span> FoodDeliverySystem:</span>
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">&quot;&quot;&quot;A system that maintains all entities (restaurants, customers, couriers, and orders).</span></span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="co"> Public Attributes:</span></span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="co"> - name: the name of this food delivery system</span></span>
<span id="cb1-6"><a href="#cb1-6"></a></span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="co"> Representation Invariants:</span></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="co"> - self.name != &#39;&#39;</span></span>
<span id="cb1-9"><a href="#cb1-9"></a><span class="co"> - all(r == self._restaurants[r].name for r in self._restaurants)</span></span>
<span id="cb1-10"><a href="#cb1-10"></a><span class="co"> - all(c == self._customers[c].name for c in self._customers)</span></span>
<span id="cb1-11"><a href="#cb1-11"></a><span class="co"> - all(c == self._couriers[c].name for c in self._couriers)</span></span>
<span id="cb1-12"><a href="#cb1-12"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb1-13"><a href="#cb1-13"></a> name: <span class="bu">str</span></span>
<span id="cb1-14"><a href="#cb1-14"></a></span>
<span id="cb1-15"><a href="#cb1-15"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb1-16"><a href="#cb1-16"></a> <span class="co"># - _restaurants: a mapping from restaurant name to Restaurant object.</span></span>
<span id="cb1-17"><a href="#cb1-17"></a> <span class="co"># This represents all the restaurants in the system.</span></span>
<span id="cb1-18"><a href="#cb1-18"></a> <span class="co"># - _customers: a mapping from customer name to Customer object.</span></span>
<span id="cb1-19"><a href="#cb1-19"></a> <span class="co"># This represents all the customers in the system.</span></span>
<span id="cb1-20"><a href="#cb1-20"></a> <span class="co"># - _couriers: a mapping from courier name to Courier object.</span></span>
<span id="cb1-21"><a href="#cb1-21"></a> <span class="co"># This represents all the couriers in the system.</span></span>
<span id="cb1-22"><a href="#cb1-22"></a> <span class="co"># - _orders: a list of all orders (both open and completed orders).</span></span>
<span id="cb1-23"><a href="#cb1-23"></a></span>
<span id="cb1-24"><a href="#cb1-24"></a> _restaurants: <span class="bu">dict</span>[<span class="bu">str</span>, Restaurant]</span>
<span id="cb1-25"><a href="#cb1-25"></a> _customers: <span class="bu">dict</span>[<span class="bu">str</span>, Customer]</span>
<span id="cb1-26"><a href="#cb1-26"></a> _couriers: <span class="bu">dict</span>[<span class="bu">str</span>, Courier]</span>
<span id="cb1-27"><a href="#cb1-27"></a> _orders: <span class="bu">list</span>[Order]</span>
<span id="cb1-28"><a href="#cb1-28"></a></span>
<span id="cb1-29"><a href="#cb1-29"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, name: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb1-30"><a href="#cb1-30"></a> <span class="co">&quot;&quot;&quot;Initialize a new food delivery system with the given company name.</span></span>
<span id="cb1-31"><a href="#cb1-31"></a></span>
<span id="cb1-32"><a href="#cb1-32"></a><span class="co"> The system starts with no entities.</span></span>
<span id="cb1-33"><a href="#cb1-33"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb1-34"><a href="#cb1-34"></a> <span class="va">self</span>.name <span class="op">=</span> name</span>
<span id="cb1-35"><a href="#cb1-35"></a></span>
<span id="cb1-36"><a href="#cb1-36"></a> <span class="va">self</span>._restaurants <span class="op">=</span> {}</span>
<span id="cb1-37"><a href="#cb1-37"></a> <span class="va">self</span>._customers <span class="op">=</span> {}</span>
<span id="cb1-38"><a href="#cb1-38"></a> <span class="va">self</span>._couriers <span class="op">=</span> {}</span>
<span id="cb1-39"><a href="#cb1-39"></a> <span class="va">self</span>._orders <span class="op">=</span> []</span></code></pre></div>
<h2 id="changing-state">Changing state</h2>
<p>What we have done so far is model the <em>static</em> properties of our food delivery system, that is, the attributes that are necessary to capture a particular snapshot of the state of the system at a specific moment in time. Next, were going to look at how to model the <em>dynamic</em> properties of the system: how the entities interact with each other and cause the system state to change over time.</p>
<h3 id="adding-entities">Adding entities</h3>
<p>Though a <code>FoodDeliverySystem</code> instance starts off empty, we can define simple methods to add entities to the system.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> You can picture this happening when a new restaurant/customer/courier signs up for our app.</span> By making our collection attributes private and requiring client code call these methods, we can check for uniqueness of these entity names as well.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">class</span> FoodDeliverySystem:</span>
<span id="cb2-2"><a href="#cb2-2"></a> ...</span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a> <span class="kw">def</span> add_restaurant(<span class="va">self</span>, restaurant: Restaurant) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="co">&quot;&quot;&quot;Add the given restaurant to this system.</span></span>
<span id="cb2-6"><a href="#cb2-6"></a></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> Do NOT add the restaurant if one with the same name already exists.</span></span>
<span id="cb2-8"><a href="#cb2-8"></a></span>
<span id="cb2-9"><a href="#cb2-9"></a><span class="co"> Return whether the restaurant was successfully added to this system.</span></span>
<span id="cb2-10"><a href="#cb2-10"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-11"><a href="#cb2-11"></a> <span class="cf">if</span> restaurant.name <span class="kw">in</span> <span class="va">self</span>._restaurants:</span>
<span id="cb2-12"><a href="#cb2-12"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb2-13"><a href="#cb2-13"></a> <span class="cf">else</span>:</span>
<span id="cb2-14"><a href="#cb2-14"></a> <span class="va">self</span>._restaurants[restaurant.name] <span class="op">=</span> restaurant</span>
<span id="cb2-15"><a href="#cb2-15"></a> <span class="cf">return</span> <span class="va">True</span></span>
<span id="cb2-16"><a href="#cb2-16"></a></span>
<span id="cb2-17"><a href="#cb2-17"></a> <span class="kw">def</span> add_customer(<span class="va">self</span>, customer: Customer) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb2-18"><a href="#cb2-18"></a> <span class="co">&quot;&quot;&quot;Add the given customer to this system.</span></span>
<span id="cb2-19"><a href="#cb2-19"></a></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="co"> Do NOT add the customer if one with the same name already exists.</span></span>
<span id="cb2-21"><a href="#cb2-21"></a></span>
<span id="cb2-22"><a href="#cb2-22"></a><span class="co"> Return whether the customer was successfully added to this system.</span></span>
<span id="cb2-23"><a href="#cb2-23"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="co"># Similar implementation to add_restaurant</span></span>
<span id="cb2-25"><a href="#cb2-25"></a></span>
<span id="cb2-26"><a href="#cb2-26"></a> <span class="kw">def</span> add_courier(<span class="va">self</span>, courier: Courier) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb2-27"><a href="#cb2-27"></a> <span class="co">&quot;&quot;&quot;Add the given courier to this system.</span></span>
<span id="cb2-28"><a href="#cb2-28"></a></span>
<span id="cb2-29"><a href="#cb2-29"></a><span class="co"> Do NOT add the courier if one with the same name already exists.</span></span>
<span id="cb2-30"><a href="#cb2-30"></a></span>
<span id="cb2-31"><a href="#cb2-31"></a><span class="co"> Return whether the courier was successfully added to this system.</span></span>
<span id="cb2-32"><a href="#cb2-32"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-33"><a href="#cb2-33"></a> <span class="co"># Similar implementation to add_restaurant</span></span></code></pre></div>
<h3 id="placing-orders">Placing orders</h3>
<p>The main driving force in our simulation is customer orders. When a customer places an order, a chain of events is triggered:</p>
<ol type="1">
<li>The order is sent to the restaurant and to the assigned courier.</li>
<li>The courier travels to the restaurant and picks up the food, and then brings it to the customer.</li>
<li>Once the courier has reached their destination, they indicate that the delivery has been made.</li>
</ol>
<p>To represent these events in our program, we need to create functions that mutate the state of the system. Where should we create these functions? We could write them as top-level functions, or as methods of one of our existing entity classes (turning that class from a data class into a general class). We have previously said that one of the roles of the <code>FoodDeliverySystem</code> is to mediate interactions between the various entities in the system, and so this makes it a natural class to add these mutating methods.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">class</span> FoodDeliverySystem:</span>
<span id="cb3-2"><a href="#cb3-2"></a> ...</span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a> <span class="kw">def</span> place_order(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb3-5"><a href="#cb3-5"></a> <span class="co">&quot;&quot;&quot;Record the new given order.</span></span>
<span id="cb3-6"><a href="#cb3-6"></a></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="co"> Assign a courier to this new order (if a courier is available).</span></span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a><span class="co"> Preconditions:</span></span>
<span id="cb3-10"><a href="#cb3-10"></a><span class="co"> - order not in self.orders</span></span>
<span id="cb3-11"><a href="#cb3-11"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-12"><a href="#cb3-12"></a></span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="kw">def</span> complete_order(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb3-14"><a href="#cb3-14"></a> <span class="co">&quot;&quot;&quot;Mark the given order as complete.</span></span>
<span id="cb3-15"><a href="#cb3-15"></a></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="co"> Make the courier who was assigned this order available to take a new order.</span></span>
<span id="cb3-17"><a href="#cb3-17"></a></span>
<span id="cb3-18"><a href="#cb3-18"></a><span class="co"> Preconditions:</span></span>
<span id="cb3-19"><a href="#cb3-19"></a><span class="co"> - order in self.orders</span></span>
<span id="cb3-20"><a href="#cb3-20"></a><span class="co"> &quot;&quot;&quot;</span></span></code></pre></div>
<p>We could then place an order from a customer using <code>FoodDeliverySystem.place_order</code>, which would be responsible for both recording the order and assigning a courier to that order. <code>FoodDeliverySystem.complete_order</code> does the opposite, marking the order as complete and un-assigning the courier so that they are free to take a new order. With both <code>FoodDeliverySystem.place_order</code> and <code>FoodDeliverySystem.complete_order</code>, we can begin to see how a simulation might take place where many customers are placing orders to different restaurants that are being delivered by available couriers.</p>
<p>Note that this discussion should make sense even though we havent implemented either of these methods. Questions like “How do we choose which courier to assign to a new order?” and “How do we mark an order as complete?” are about <em>implementation</em> rather than the public interface of these methods. Well discuss one potential implementation of these methods in lecture, but we welcome you to attempt your own implementations as an exercise.</p>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+356
View File
@@ -0,0 +1,356 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>10.4 Food Delivery Events</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">10.4 Food Delivery Events</h1>
</header>
<section>
<p>In the previous two sections, we discussed the key classes we can use to represent a food delivery system: data classes <code>Restaurant</code>, <code>Customer</code>, <code>Courier</code>, and <code>Order</code> to represent individual entities, and a <code>FoodDeliverySystem</code> class to manage all of them. But even though the <code>FoodDeliverySystem</code> class has methods that allow us to <em>mutate</em> the state of the system, you might wonder: who is responsible for actually calling these methods?</p>
<p>If we were building a “real-world” app, we would need to write code that explicitly connects user actions (e.g., pressing a button on a mobile app) to these methods, and almost certainly rely on an existing software framework to do much of the “connecting” for us.</p>
<p>The approach were taking in this chapter is a bit different. Instead of writing the code necessary to respond to real-world actions, we are going to create a <em>simulation</em> that uses a combination of preset and random data to simulate these kinds of real-world actions. The driving force of our simulation will be <em>events</em> that cause our system to mutate. For example, a “new order” event for when a customer places an order, and a “complete order” event for when a courier has delivered an order to a customer.</p>
<h2 id="the-event-interface">The <code>Event</code> interface</h2>
<p>There are many other events we might add to the simulation, but they clearly have something in common: they are events that cause the state of the simulation to change. In <a href="../09-abstraction/08-common-interfaces.html">9.8 Defining a Shared Public Interface with Inheritance</a>, we learned how to define an abstract class to represent a shared public interface, and used inheritance to relate this abstract class to concrete subclasses that must adhere to this interface. In our case, well define abstract <code>Event</code> class with subclasses <code>NewOrderEvent</code> and <code>CompleteOrderEvent</code> to represent different kinds of events.</p>
<p>Here is an initial definition of this <code>Event</code> interface. The class has one abstract method, <code>handle_event</code>, which is how we connect each event to a change in the food delivery system.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">class</span> Event:</span>
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">&quot;&quot;&quot;An abstract class representing an event in a food delivery simulation.</span></span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb1-4"><a href="#cb1-4"></a></span>
<span id="cb1-5"><a href="#cb1-5"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="co">&quot;&quot;&quot;Mutate the given food delivery system to process this event.&quot;&quot;&quot;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> <span class="cf">raise</span> <span class="pp">NotImplementedError</span></span></code></pre></div>
<p>Each <code>Event</code> subclass is responsible for implementing <code>handle_event</code> based on the type of change the subclass represents. For example, the <code>NewOrderEvent.handle_event</code> method should, well, add a new order to the system. In order to implement <code>handle_event</code>, each subclass will probably need its own set of instance attributes to represent the details of the event (e.g., what order to add in a <code>NewOrderEvent</code>).</p>
<p>But before we discuss these subclass-specific attributes, well take a brief detour well introduce another feature of inheritance: shared instance attributes. Specifically, our simulation will need to know exactly <em>when</em> every event should happen, which every event object needs to keep track of.</p>
<h2 id="common-instance-attributes">Common instance attributes</h2>
<p>We have seen that an abstract superclass declare methods that all its subclasses need to have in common, establishing a shared public interface. A superclass can also declare <em>public instance attributes</em> that its subclasses must have in common. For our <code>Event</code> class, we can establish that all event subclasses will have a timestamp indicating when the event took place. This <code>timestamp</code> attribute becomes part of the shared public interface of each subclass.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="im">import</span> datetime</span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="kw">class</span> Event:</span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="co">&quot;&quot;&quot;An abstract class representing an event in a food delivery simulation.</span></span>
<span id="cb2-6"><a href="#cb2-6"></a></span>
<span id="cb2-7"><a href="#cb2-7"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb2-8"><a href="#cb2-8"></a><span class="co"> - timestamp: the start time of the event</span></span>
<span id="cb2-9"><a href="#cb2-9"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> timestamp: datetime.datetime</span></code></pre></div>
<p>Even though abstract classes should not be instantiated directly, we provide an initializer to initialize the common attributes (namely, <code>timestamp</code>):</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="im">import</span> datetime</span>
<span id="cb3-2"><a href="#cb3-2"></a></span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="kw">class</span> Event:</span>
<span id="cb3-5"><a href="#cb3-5"></a> <span class="co">&quot;&quot;&quot;An abstract class representing an event in a food delivery simulation.</span></span>
<span id="cb3-6"><a href="#cb3-6"></a></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="co"> Instance Attributes:</span></span>
<span id="cb3-8"><a href="#cb3-8"></a><span class="co"> - timestamp: the start time of the event</span></span>
<span id="cb3-9"><a href="#cb3-9"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-10"><a href="#cb3-10"></a> timestamp: datetime.datetime</span>
<span id="cb3-11"><a href="#cb3-11"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, timestamp: datetime.datetime) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="co">&quot;&quot;&quot;Initialize this event with the given timestamp.&quot;&quot;&quot;</span></span>
<span id="cb3-14"><a href="#cb3-14"></a> <span class="va">self</span>.timestamp <span class="op">=</span> timestamp</span></code></pre></div>
<p>Now lets create a new class that inherits from <code>Event</code>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;An event where a customer places an order for a restaurant.&quot;&quot;&quot;</span></span></code></pre></div>
<p>Remember that subclasses will inherit all the methods from their superclass. So when we attempt to initialize a <code>NewOrderEvent</code>, the Python interpreter will call <code>Event.__init__</code> (because <code>NewOrderEvent</code> did not override the parents <code>__init__</code> method). This means we <em>must</em> provide a <code>datetime.datetime</code> object as the first argument when creating a new <code>NewOrderEvent</code> object:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a><span class="op">&gt;&gt;&gt;</span> e <span class="op">=</span> NewOrderEvent()</span>
<span id="cb5-2"><a href="#cb5-2"></a>Traceback (most recent call last):</span>
<span id="cb5-3"><a href="#cb5-3"></a> File <span class="st">&quot;&lt;input&gt;&quot;</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op">&lt;</span>module<span class="op">&gt;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a><span class="pp">TypeError</span>: <span class="fu">__init__</span>() missing <span class="dv">1</span> required positional argument: <span class="st">&#39;timestamp&#39;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="op">&gt;&gt;&gt;</span> e <span class="op">=</span> NewOrderEvent(datetime.datetime(<span class="dv">2020</span>, <span class="dv">9</span>, <span class="dv">8</span>))</span>
<span id="cb5-6"><a href="#cb5-6"></a><span class="op">&gt;&gt;&gt;</span> e.timestamp</span>
<span id="cb5-7"><a href="#cb5-7"></a>datetime.datetime(<span class="dv">2020</span>, <span class="dv">7</span>, <span class="dv">20</span>, <span class="dv">0</span>, <span class="dv">0</span>)</span></code></pre></div>
<h2 id="subclass-specific-attributes">Subclass-specific attributes</h2>
<p>It is possible that subclasses need their own attributes in addition to the ones that are common through the base class. In these scenarios, we should document our new attributes in the subclass itself. We often make these attributes private, to avoid changing the public interface declared by the abstract superclass. We do <em>not</em> need to repeat the documentation for the <code>timestamp</code> attribute; our expectation is that users should read the documentation of both the <code>NewOrderEvent</code> and <code>Event</code> classes to get the full picture of how <code>NewOrderEvent</code> is used.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb6-2"><a href="#cb6-2"></a> <span class="co">&quot;&quot;&quot;An event representing a when a customer places an order at a restaurant.&quot;&quot;&quot;</span></span>
<span id="cb6-3"><a href="#cb6-3"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> <span class="co"># _order: the new order to be added to the FoodDeliverySystem</span></span>
<span id="cb6-5"><a href="#cb6-5"></a> _order: Order</span></code></pre></div>
<p>To initialize this new attribute, we must define a separate initializer for <code>NewOrderEvent</code>. Here is our first attempt:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb7-2"><a href="#cb7-2"></a> <span class="co">&quot;&quot;&quot;An event representing a when a customer places an order at a restaurant.&quot;&quot;&quot;</span></span>
<span id="cb7-3"><a href="#cb7-3"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb7-4"><a href="#cb7-4"></a> <span class="co"># _order: the new order to be added to the FoodDeliverySystem</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> _order: Order</span>
<span id="cb7-6"><a href="#cb7-6"></a></span>
<span id="cb7-7"><a href="#cb7-7"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb7-8"><a href="#cb7-8"></a> <span class="co">&quot;&quot;&quot;Initialize a NewOrderEvent for the given order.&quot;&quot;&quot;</span></span>
<span id="cb7-9"><a href="#cb7-9"></a> <span class="va">self</span>._order <span class="op">=</span> order</span></code></pre></div>
<p>This code looks correct, but has a subtle bug. By defining our own initializer for <code>NewOrderEvent</code>, we have overridden the <code>Event.__init__</code> method. Python will no longer call <code>Event.__init__</code> when creating a new <code>NewOrderEvent</code> object. However, this is problematic because <strong>subclasses inherit methods, not attributes.</strong> This means that the public instance attribute <code>timestamp</code> is missing from our <code>NewOrderEvent</code> object:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1"></a><span class="op">&gt;&gt;&gt;</span> order <span class="op">=</span> ... <span class="co"># Assume we&#39;ve defined an Order object here</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="op">&gt;&gt;&gt;</span> event <span class="op">=</span> NewOrderEvent(order)</span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="op">&gt;&gt;&gt;</span> event.timestamp</span>
<span id="cb8-4"><a href="#cb8-4"></a>Traceback (most recent call last):</span>
<span id="cb8-5"><a href="#cb8-5"></a> File <span class="st">&quot;&lt;input&gt;&quot;</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op">&lt;</span>module<span class="op">&gt;</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="pp">AttributeError</span>: <span class="st">&#39;NewOrderEvent&#39;</span> <span class="bu">object</span> has no attribute <span class="st">&#39;timestamp&#39;</span></span></code></pre></div>
<p>So how do we make <code>NewOrderEvent</code> have both an <code>_order</code> and <code>timestamp</code> attribute? We need to modify its initializer, since it is the responsibility of the initializer to give values to all instance attributes.</p>
<p>First, what <em>should</em> the value of the events <code>timestamp</code> be? A natural choice is that it should be the time that the order was placed—its <code>start_time</code> attribute. Here is our second attempt at the <code>NewOrderEvent.__init__</code> method:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb9-3"><a href="#cb9-3"></a> <span class="va">self</span>.timestamp <span class="op">=</span> order.start_time</span>
<span id="cb9-4"><a href="#cb9-4"></a> <span class="va">self</span>._order <span class="op">=</span> order</span></code></pre></div>
<p>However, initializing the <code>timestamp</code> attribute directly in the subclass is bad design; code has been duplicated and that makes <a href="https://en.wikipedia.org/wiki/Code_smell">our code smell bad</a>. Every time we modify the <code>Event</code> class to include new shared attributes, wed also need to modify <code>NewOrderEvent.__init__</code> (and the initializers of every other subclass) to initialize those attributes.</p>
<p>So instead, we modify <code>NewOrderEvent.__init__</code> so that it directly calls <code>Event.__init__</code>.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> Remember that when we call a method using the <code>&lt;Class&gt;.&lt;method&gt;</code> name, we need to pass in the <code>self</code> argument explicitly.</span> Here is our third and final version of this initializer:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb10-2"><a href="#cb10-2"></a> <span class="co">&quot;&quot;&quot;An event where a customer places an order for a restaurant.&quot;&quot;&quot;</span></span>
<span id="cb10-3"><a href="#cb10-3"></a> _order: Order</span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb10-6"><a href="#cb10-6"></a> Event.<span class="fu">__init__</span>(<span class="va">self</span>, order.start_time)</span>
<span id="cb10-7"><a href="#cb10-7"></a> <span class="va">self</span>._order <span class="op">=</span> order</span></code></pre></div>
<p>Now, whenever we call <code>NewOrderEvent.__init__</code>, Python also calls <code>Event.__init__</code>. This causes all shared instance attributes from <code>Event</code> to be “inherited” by the <code>NewOrderEvent</code> subclass.</p>
<p>To summarize, we must follow two rules when inheriting from a class that defines its own initializer:</p>
<ol type="1">
<li>The initializer of a subclass must call the initializer of its superclass to initialize all common attributes.</li>
<li>The initializer of a subclass is responsible for initializing any additional attributes that are specific to that subclass.</li>
</ol>
<h2 id="implementing-neworderevent.handle_event">Implementing <code>NewOrderEvent.handle_event</code></h2>
<p>Next, well show how to complete the implementation of <code>NewOrderEvent</code> by implementing its <code>handle_event</code> method. Our first attempt is quite simple, taking advantage of the methods we defined in <a href="03-manager-class.html">10.3 A “Manager” Class</a>.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb11-2"><a href="#cb11-2"></a> <span class="co">&quot;&quot;&quot;An event where a customer places an order for a restaurant.&quot;&quot;&quot;</span></span>
<span id="cb11-3"><a href="#cb11-3"></a> _order: Order</span>
<span id="cb11-4"><a href="#cb11-4"></a></span>
<span id="cb11-5"><a href="#cb11-5"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb11-6"><a href="#cb11-6"></a> Event.<span class="fu">__init__</span>(<span class="va">self</span>, timestamp)</span>
<span id="cb11-7"><a href="#cb11-7"></a> <span class="va">self</span>._order <span class="op">=</span> order</span>
<span id="cb11-8"><a href="#cb11-8"></a></span>
<span id="cb11-9"><a href="#cb11-9"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb11-10"><a href="#cb11-10"></a> <span class="co">&quot;&quot;&quot;Mutate system by placing an order.&quot;&quot;&quot;</span></span>
<span id="cb11-11"><a href="#cb11-11"></a> system.place_order(<span class="va">self</span>._order)</span></code></pre></div>
<p>Now, theres a subtle problem with this method that well return to at the end of this section. A good exercise is to pause here and try to think about what the problem might be.</p>
<h2 id="implementing-other-event-subclass">Implementing other <code>Event</code> subclass</h2>
<p>Below, weve shown the implementation of our <code>CompleteOrderEvent</code>, which is quite similar to <code>newOrderEvent</code>. The major difference is that its initializer takes an explicit <code>datetime.datetime</code> argument to represent when the given order is completed.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> By convention, the <code>timestamp</code> parameter is the first parameter, so that the subsequent parameters are seen as additional parameters needed by <code>NewOrderEvent</code> rather than <code>Event</code>. This example shows that initializers of subclasses can have different signatures than the initializer of their parent class.</span></p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">class</span> CompleteOrderEvent(Event):</span>
<span id="cb12-2"><a href="#cb12-2"></a> <span class="co">&quot;&quot;&quot;When an order is delivered to a customer by a courier.&quot;&quot;&quot;</span></span>
<span id="cb12-3"><a href="#cb12-3"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb12-4"><a href="#cb12-4"></a> <span class="co"># _order: the order to be completed by this event</span></span>
<span id="cb12-5"><a href="#cb12-5"></a> _order: Order</span>
<span id="cb12-6"><a href="#cb12-6"></a></span>
<span id="cb12-7"><a href="#cb12-7"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, timestamp: datetime.datetime, order: Order) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb12-8"><a href="#cb12-8"></a> Event.<span class="fu">__init__</span>(<span class="va">self</span>, timestamp)</span>
<span id="cb12-9"><a href="#cb12-9"></a> <span class="va">self</span>._order <span class="op">=</span> order</span>
<span id="cb12-10"><a href="#cb12-10"></a></span>
<span id="cb12-11"><a href="#cb12-11"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb12-12"><a href="#cb12-12"></a> <span class="co">&quot;&quot;&quot;Mutate the system by recording that the order has been delivered to the customer.&quot;&quot;&quot;</span></span>
<span id="cb12-13"><a href="#cb12-13"></a> system.complete_order(<span class="va">self</span>._order, <span class="va">self</span>.timestamp)</span></code></pre></div>
<h2 id="event-generation">Event generation</h2>
<p>We started off this section by asking, “when are the <code>FoodDeliverySystem</code> methods called”? We said that our simulation would have <code>Event</code> instances that would be responsible for calling these methods. But this really just changes the direction of our original question—it now becomes, “when are the <code>Event</code> instances created?”</p>
<p>One possible approach is to randomly create a whole set of events at the start of our simulation, and then process each of those events (in order of their <code>timestamp</code>). This approach works when the events are fairly simple and can be predictably generated all at once. However, one key feature of events in general is that <em>processing one event can cause other events to occur</em>. For example, when we process a <code>NewOrderEvent</code>, we expect that at some point in the future, a corresponding <code>CompleteOrderEvent</code> will occur.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> Once the delivery is started, it completes. This doesnt necessarily always happen in real life, but well assume it does for the purposes of this case study.</span></p>
<p>To model this behaviour, we change the return type of <code>handle_event</code> from <code>None</code> to <code>list[Event]</code>, where the return value is a list of the events <em>caused</em> by the current event.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1"></a><span class="kw">class</span> Event:</span>
<span id="cb13-2"><a href="#cb13-2"></a> ...</span>
<span id="cb13-3"><a href="#cb13-3"></a></span>
<span id="cb13-4"><a href="#cb13-4"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="bu">list</span>[Event]:</span>
<span id="cb13-5"><a href="#cb13-5"></a> <span class="co">&quot;&quot;&quot;Mutate the given food delivery system to process this event.</span></span>
<span id="cb13-6"><a href="#cb13-6"></a></span>
<span id="cb13-7"><a href="#cb13-7"></a><span class="co"> Return a new list of new events created by processing this event.</span></span>
<span id="cb13-8"><a href="#cb13-8"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb13-9"><a href="#cb13-9"></a> <span class="cf">raise</span> <span class="pp">NotImplementedError</span></span></code></pre></div>
<p>Heres how we might change the <code>NewOrderEvent</code> to return a <code>CompleteOrderEvent</code> at some point in the future.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb14-2"><a href="#cb14-2"></a> ...</span>
<span id="cb14-3"><a href="#cb14-3"></a></span>
<span id="cb14-4"><a href="#cb14-4"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="bu">list</span>[Event]:</span>
<span id="cb14-5"><a href="#cb14-5"></a> <span class="co">&quot;&quot;&quot;Mutate system by placing an order.&quot;&quot;&quot;</span></span>
<span id="cb14-6"><a href="#cb14-6"></a> system.place_order(<span class="va">self</span>._order)</span>
<span id="cb14-7"><a href="#cb14-7"></a></span>
<span id="cb14-8"><a href="#cb14-8"></a> <span class="co"># Create a new CompleteOrderEvent. Right now the completion time is</span></span>
<span id="cb14-9"><a href="#cb14-9"></a> <span class="co"># hard-coded as 10 minutes from the order creation.</span></span>
<span id="cb14-10"><a href="#cb14-10"></a> <span class="co"># How could be make this more realistic by taking into account the</span></span>
<span id="cb14-11"><a href="#cb14-11"></a> <span class="co"># positions of the courier, customer, and restaurant?</span></span>
<span id="cb14-12"><a href="#cb14-12"></a> completion_time <span class="op">=</span> <span class="va">self</span>.timestamp <span class="op">+</span> datetime.timedelta(minutes<span class="op">=</span><span class="dv">10</span>)</span>
<span id="cb14-13"><a href="#cb14-13"></a> <span class="cf">return</span> [CompleteOrderEvent(completion_time, <span class="va">self</span>._order)]</span></code></pre></div>
<p>So for every <code>NewOrderEvent</code> that is handled by our simulation, a subsequent <code>CompleteOrderEvent</code> will be handled at some point in the future.</p>
<p><em>Now heres where the problem we mentioned earlier comes in!</em> Remember our docstring for <code>FoodDeliverySystem.place_order</code>: we cannot place an order if there are no available couriers! So what should this event do if <code>system.place_order</code> returns <code>False</code>? At the very least, in this case no <code>CompleteOrderEvent</code> should be returned.</p>
<p>One approach we might take is a <em>polling technique</em>, where we return a duplicate of the event to try again a little bit later. Here is our second version of this method:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1"></a><span class="kw">class</span> NewOrderEvent(Event):</span>
<span id="cb15-2"><a href="#cb15-2"></a> ...</span>
<span id="cb15-3"><a href="#cb15-3"></a></span>
<span id="cb15-4"><a href="#cb15-4"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="bu">list</span>[Event]:</span>
<span id="cb15-5"><a href="#cb15-5"></a> <span class="co">&quot;&quot;&quot;Mutate system by placing an order.&quot;&quot;&quot;</span></span>
<span id="cb15-6"><a href="#cb15-6"></a> success <span class="op">=</span> system.place_order(<span class="va">self</span>._order)</span>
<span id="cb15-7"><a href="#cb15-7"></a></span>
<span id="cb15-8"><a href="#cb15-8"></a> <span class="cf">if</span> success:</span>
<span id="cb15-9"><a href="#cb15-9"></a> completion_time <span class="op">=</span> <span class="va">self</span>.timestamp <span class="op">+</span> datetime.timedelta(minutes<span class="op">=</span><span class="dv">10</span>)</span>
<span id="cb15-10"><a href="#cb15-10"></a> <span class="cf">return</span> [CompleteOrderEvent(completion_time, <span class="va">self</span>._order)]</span>
<span id="cb15-11"><a href="#cb15-11"></a> <span class="cf">else</span>:</span>
<span id="cb15-12"><a href="#cb15-12"></a> <span class="va">self</span>._order.start_time <span class="op">=</span> <span class="va">self</span>.timestamp <span class="op">+</span> datetime.timedelta(minutes<span class="op">=</span><span class="dv">5</span>)</span>
<span id="cb15-13"><a href="#cb15-13"></a> <span class="cf">return</span> [NewOrderEvent(<span class="va">self</span>._order)]</span></code></pre></div>
<h3 id="returning-no-events">Returning no events</h3>
<p>Our <code>CompleteOrderEvent</code> does not cause any new events to happen:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1"></a><span class="kw">class</span> CompleteOrderEvent(Event):</span>
<span id="cb16-2"><a href="#cb16-2"></a> ...</span>
<span id="cb16-3"><a href="#cb16-3"></a></span>
<span id="cb16-4"><a href="#cb16-4"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="bu">list</span>[Event]:</span>
<span id="cb16-5"><a href="#cb16-5"></a> <span class="co">&quot;&quot;&quot;Mutate the system by recording that the order has been delivered to the customer.&quot;&quot;&quot;</span></span>
<span id="cb16-6"><a href="#cb16-6"></a> system.complete_order(<span class="va">self</span>._order, <span class="va">self</span>._timestamp)</span>
<span id="cb16-7"><a href="#cb16-7"></a> <span class="cf">return</span> []</span></code></pre></div>
<h3 id="a-new-event-type">A new event type</h3>
<p>Lastly, well sketch one new type of event which is more conceptual, but that illustrates the power of this <code>Event</code> interface. This event type will represent a <em>random generation of new orders over a given time period</em>, which well use to drive our simulation.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1"></a><span class="kw">class</span> GenerateOrdersEvent(Event):</span>
<span id="cb17-2"><a href="#cb17-2"></a> <span class="co">&quot;&quot;&quot;An event that causes a random generation of new orders.</span></span>
<span id="cb17-3"><a href="#cb17-3"></a></span>
<span id="cb17-4"><a href="#cb17-4"></a><span class="co"> Private Representation Invariants:</span></span>
<span id="cb17-5"><a href="#cb17-5"></a><span class="co"> - self._duration &gt; 0</span></span>
<span id="cb17-6"><a href="#cb17-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb17-7"><a href="#cb17-7"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb17-8"><a href="#cb17-8"></a> <span class="co"># - _duration: the number of hours to generate orders for</span></span>
<span id="cb17-9"><a href="#cb17-9"></a> _duration: <span class="bu">int</span></span>
<span id="cb17-10"><a href="#cb17-10"></a></span>
<span id="cb17-11"><a href="#cb17-11"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, timestamp: datetime.datetime, duration: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb17-12"><a href="#cb17-12"></a> <span class="co">&quot;&quot;&quot;Initialize this event with timestamp and the duration in hours.</span></span>
<span id="cb17-13"><a href="#cb17-13"></a></span>
<span id="cb17-14"><a href="#cb17-14"></a><span class="co"> Preconditions:</span></span>
<span id="cb17-15"><a href="#cb17-15"></a><span class="co"> - duration &gt; 0</span></span>
<span id="cb17-16"><a href="#cb17-16"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb17-17"><a href="#cb17-17"></a></span>
<span id="cb17-18"><a href="#cb17-18"></a> <span class="kw">def</span> handle_event(<span class="va">self</span>, system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="bu">list</span>[Event]:</span>
<span id="cb17-19"><a href="#cb17-19"></a> <span class="co">&quot;&quot;&quot;Generate new orders for this event&#39;s timestamp and duration.&quot;&quot;&quot;</span></span>
<span id="cb17-20"><a href="#cb17-20"></a> events <span class="op">=</span> []</span>
<span id="cb17-21"><a href="#cb17-21"></a></span>
<span id="cb17-22"><a href="#cb17-22"></a> <span class="cf">while</span> ...:</span>
<span id="cb17-23"><a href="#cb17-23"></a></span>
<span id="cb17-24"><a href="#cb17-24"></a> new_order_event <span class="op">=</span> ... <span class="co"># Create a randomly-generated NewOrderEvent</span></span>
<span id="cb17-25"><a href="#cb17-25"></a> events.append(new_order_event)</span>
<span id="cb17-26"><a href="#cb17-26"></a></span>
<span id="cb17-27"><a href="#cb17-27"></a> <span class="cf">return</span> events</span></code></pre></div>
<p>Well discuss how we might implement this class in lecture, but its a good exercise to try to implement it yourself. Theres many ways to randomly generate new events, so dont be afraid to experiment!</p>
<h2 id="from-events-to-a-simulation">From events to a simulation</h2>
<p>In this section, we focused only on defining individual <code>Event</code> classes to represent different events in our simulation. In the next section, well put together everything weve covered up to this point to finally get a full simulation up and running, so keep reading!</p>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>
+276
View File
@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>10.5 Creating a Discrete-Event Simulation</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">10.5 Creating a Discrete-Event Simulation</h1>
</header>
<section>
<p>Lets put together all of the classes weve designed over the course of this chapter to create a full simulation our of food delivery system. In this section, well first learn about how the main simulation loop works. Then, well turn our attention to the possible ways a simulation can be configured, and how to incorporate these configuration options as part of the public interface of a class.</p>
<h2 id="the-main-simulation-loop">The main simulation loop</h2>
<p>Before we get to creating a full simulation class, well discuss how our simulation works. The type of simulation were learning about is called a <strong>discrete-event simulation</strong>, because it is driven by individual events occurring at specified periods of time.</p>
<p>A discrete-event simulation runs as follows:</p>
<ol type="1">
<li>It keeps track of a collection of events, which begins with some initial events. The collection is a <em>priority queue</em>, where an events priority is its timestamp (earlier timestamps mean higher priority).</li>
<li>The highest-priority event (i.e., the one with the earliest timestamp) is removed and processed. Any new events it generates are added to the priority queue.</li>
<li>Step 2 repeats until there are no events left.</li>
</ol>
<p>The algorithm is remarkably simple, though it does rely on a slightly modified version of our <em>priority queue</em> implementation from <a href="" title="../09-abstraction/07-priority-queues.html">Section 9.7</a>.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote">In that section, we used <code>int</code>s to represent priority, while here were using <code>datetime.datetime</code> values.</span> Assuming we have such an implementation called <code>EventQueueList</code>, here is how we could write a simple function that runs this simulation loop:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> run_simulation(initial_events: <span class="bu">list</span>[Event], system: FoodDeliverySystem) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb1-2"><a href="#cb1-2"></a> events <span class="op">=</span> EventQueueList() <span class="co"># Initialize an empty priority queue of events</span></span>
<span id="cb1-3"><a href="#cb1-3"></a> <span class="cf">for</span> event <span class="kw">in</span> initial_events:</span>
<span id="cb1-4"><a href="#cb1-4"></a> events.enqueue(event)</span>
<span id="cb1-5"><a href="#cb1-5"></a></span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="co"># Repeatedly remove and process the next event</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> <span class="cf">while</span> <span class="kw">not</span> events.is_empty():</span>
<span id="cb1-8"><a href="#cb1-8"></a> event <span class="op">=</span> events.dequeue()</span>
<span id="cb1-9"><a href="#cb1-9"></a></span>
<span id="cb1-10"><a href="#cb1-10"></a> new_events <span class="op">=</span> event.handle_event(system)</span>
<span id="cb1-11"><a href="#cb1-11"></a> <span class="cf">for</span> new_event <span class="kw">in</span> new_events:</span>
<span id="cb1-12"><a href="#cb1-12"></a> events.enqueue(new_event)</span></code></pre></div>
<p>The main reason for this implementations simplicity is abstraction. Remember that <code>Event</code> is an abstract class; the complex behaviour of how different events are handled is deferred to its concrete subclasses via our calls to <code>event.handle_event</code>. Our <code>run_simulation</code> function is <em>polymorphic</em>: it works regardless of what <code>Event</code> instances its given in its <code>initial_events</code> parameter, or what new events are generated and stored in <code>new_events</code>. The only thing our function needs to be able to do is call the <code>handle_event</code> method on each event object, which we can assume is present because it is defined in the <code>Event</code> public interface.</p>
<h2 id="a-simulation-class">A simulation class</h2>
<p>Next, we will take our <code>run_simulation</code> in the previous section and “wrap” it inside a new class. This isnt necessary to the running of the simulation, but is a standard practice in an object-oriented design, and makes it easier to both configure the simulation parameters and report results after the simulation is complete.</p>
<p>Were going to begin with a sketch of a class to represent our simulation:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">class</span> FoodDeliverySimulation:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;A simulation of the food delivery system.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> <span class="co"># Private Instance Attributes:</span></span>
<span id="cb2-5"><a href="#cb2-5"></a> <span class="co"># - _system: The FoodDeliverySystem instance that this simulation uses.</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> <span class="co"># - _events: A collection of the events to process during the simulation.</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> _system: FoodDeliverySystem</span>
<span id="cb2-8"><a href="#cb2-8"></a> _events: EventQueue</span>
<span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, start_time: datetime.datetime, num_days: <span class="bu">int</span>,</span>
<span id="cb2-11"><a href="#cb2-11"></a> num_couriers: <span class="bu">int</span>, num_customers: <span class="bu">int</span>,</span>
<span id="cb2-12"><a href="#cb2-12"></a> num_restaurants: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-13"><a href="#cb2-13"></a> <span class="co">&quot;&quot;&quot;Initialize a new simulation with the given simulation parameters.</span></span>
<span id="cb2-14"><a href="#cb2-14"></a></span>
<span id="cb2-15"><a href="#cb2-15"></a><span class="co"> start_time: the starting time of the simulation</span></span>
<span id="cb2-16"><a href="#cb2-16"></a><span class="co"> num_days: the number of days that the simulation runs</span></span>
<span id="cb2-17"><a href="#cb2-17"></a><span class="co"> num_couriers: the number of couriers in the system</span></span>
<span id="cb2-18"><a href="#cb2-18"></a><span class="co"> num_customers: the number of customers in the system</span></span>
<span id="cb2-19"><a href="#cb2-19"></a><span class="co"> num_restaurants: the number of restaurants in the system</span></span>
<span id="cb2-20"><a href="#cb2-20"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-21"><a href="#cb2-21"></a> <span class="va">self</span>._events <span class="op">=</span> EventQueueList()</span>
<span id="cb2-22"><a href="#cb2-22"></a> <span class="va">self</span>._system <span class="op">=</span> FoodDeliverySystem()</span>
<span id="cb2-23"><a href="#cb2-23"></a></span>
<span id="cb2-24"><a href="#cb2-24"></a> <span class="va">self</span>._populate_initial_events(start_time, num_days)</span>
<span id="cb2-25"><a href="#cb2-25"></a> <span class="va">self</span>._generate_system(num_couriers, num_customers, num_restaurants)</span>
<span id="cb2-26"><a href="#cb2-26"></a></span>
<span id="cb2-27"><a href="#cb2-27"></a> <span class="kw">def</span> _populate_initial_events(<span class="va">self</span>, start_time: datetime.datetime, num_days: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-28"><a href="#cb2-28"></a> <span class="co">&quot;&quot;&quot;Populate this simulation&#39;s Event priority queue with GenerateOrdersEvents.</span></span>
<span id="cb2-29"><a href="#cb2-29"></a></span>
<span id="cb2-30"><a href="#cb2-30"></a><span class="co"> One new GenerateOrderEvent is generated per day, starting with start_time and</span></span>
<span id="cb2-31"><a href="#cb2-31"></a><span class="co"> repeating num_days times.</span></span>
<span id="cb2-32"><a href="#cb2-32"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-33"><a href="#cb2-33"></a></span>
<span id="cb2-34"><a href="#cb2-34"></a> <span class="kw">def</span> _generate_system(<span class="va">self</span>, num_couriers: <span class="bu">int</span>, num_customers: <span class="bu">int</span>, num_restaurants: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-35"><a href="#cb2-35"></a> <span class="co">&quot;&quot;&quot;Populate this simulation&#39;s FoodDeliverySystem with the specified number of entities.</span></span>
<span id="cb2-36"><a href="#cb2-36"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-37"><a href="#cb2-37"></a></span>
<span id="cb2-38"><a href="#cb2-38"></a> <span class="kw">def</span> run(<span class="va">self</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb2-39"><a href="#cb2-39"></a> <span class="co">&quot;&quot;&quot;Run this simulation.</span></span>
<span id="cb2-40"><a href="#cb2-40"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb2-41"><a href="#cb2-41"></a> <span class="cf">while</span> <span class="kw">not</span> <span class="va">self</span>._events.is_empty():</span>
<span id="cb2-42"><a href="#cb2-42"></a> event <span class="op">=</span> <span class="va">self</span>._events.dequeue()</span>
<span id="cb2-43"><a href="#cb2-43"></a></span>
<span id="cb2-44"><a href="#cb2-44"></a> new_events <span class="op">=</span> event.handle_event(<span class="va">self</span>._system)</span>
<span id="cb2-45"><a href="#cb2-45"></a> <span class="cf">for</span> new_event <span class="kw">in</span> new_events:</span>
<span id="cb2-46"><a href="#cb2-46"></a> <span class="va">self</span>._events.enqueue(new_event)</span></code></pre></div>
<p>There are a few key items to note in this (incomplete) implementation:</p>
<ol type="1">
<li><p>The <code>run_simulation</code> method has been renamed to simply <code>run</code>, since its a method in the <code>FoodDeliverySimulation</code> class.</p></li>
<li><p>The local variable <code>events</code> and parameter <code>system</code> from the function are now instance attributes for the <code>FoodDeliverySimulation</code> class, and have been moved out of the <code>run</code> method entirely. Its the job of <code>FoodDeliverySimulation.__init__</code> to initialize these objects.</p></li>
<li><p>The initializer takes in several parameters representing <em>configuration values</em> for the simulation. It then uses these values in two helper methods to initialize the <code>_system</code> and <code>_events</code> objects. These methods are marked private (named with a leading underscore) because theyre only meant to be called by the initializer, and not code outside of the class.</p>
<p>Here is how we could use the <code>FoodDeliverySimulation</code> class:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="op">&gt;&gt;&gt;</span> simulation <span class="op">=</span> FoodDeliverySimulation(datetime.datetime(<span class="dv">2020</span>, <span class="dv">11</span>, <span class="dv">30</span>), <span class="dv">7</span>, <span class="dv">4</span>, <span class="dv">100</span>, <span class="dv">50</span>)</span>
<span id="cb3-2"><a href="#cb3-2"></a><span class="op">&gt;&gt;&gt;</span> simulation.run()</span></code></pre></div></li>
</ol>
<p>Next, well briefly discuss one way to implement each of the two key helper methods for the initializer, <code>_populate_initial_events</code> and <code>_generate_system</code>.</p>
<h3 id="populating-initial-events">Populating initial events</h3>
<p>The key idea for our first helper method is that given a start time and a number of days, our initial events will be a series of <code>GenerateOrderEvents</code> that will generate <code>NewOrderEvents</code> when they are processed. Here is the basic skeleton, which will be leave as an exercise for you to complete:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a> <span class="kw">def</span> _populate_initial_events(<span class="va">self</span>, start_time: datetime.datetime, num_days: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;Populate this simulation&#39;s Event priority queue with GenerateOrdersEvents.</span></span>
<span id="cb4-3"><a href="#cb4-3"></a></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> One new GenerateOrderEvent is generated per day, starting with start_time and</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="co"> repeating num_days times.</span></span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="cf">for</span> day <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, num_days):</span>
<span id="cb4-8"><a href="#cb4-8"></a> <span class="co"># 1. Create a GenerateOrderEvent for the given day after the start time.</span></span>
<span id="cb4-9"><a href="#cb4-9"></a></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="co"># 2. Enqueue the new event.</span></span></code></pre></div>
<h3 id="populating-the-system-entities">Populating the system entities</h3>
<p>The way that our simulation is currently set up, our <code>FoodDeliverySystem</code> instance will contain all restaurants, customers, and couriers before the events start being processed. That is, we assume that only <em>orders</em> are dynamic in our system; the restaurants, customers, and couriers do not change over time.</p>
<p>The easiest way to populate these three entity types is to randomly generate new instances of each of these classes. Weve shown an example with <code>Customer</code>s below.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1"></a> <span class="kw">def</span> _generate_system(<span class="va">self</span>, num_couriers: <span class="bu">int</span>, num_customers: <span class="bu">int</span>, num_restaurants: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb5-2"><a href="#cb5-2"></a> <span class="co">&quot;&quot;&quot;Populate this simulation&#39;s FoodDeliverySystem with the specified number of entities.</span></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a> <span class="cf">for</span> i <span class="kw">in</span> <span class="bu">range</span>(<span class="dv">0</span>, num_customers):</span>
<span id="cb5-5"><a href="#cb5-5"></a> location <span class="op">=</span> _generate_location()</span>
<span id="cb5-6"><a href="#cb5-6"></a> customer <span class="op">=</span> Customer(<span class="ss">f&#39;Customer </span><span class="sc">{i}</span><span class="ss">&#39;</span>, location)</span>
<span id="cb5-7"><a href="#cb5-7"></a> <span class="va">self</span>._system.add_customer(customer)</span>
<span id="cb5-8"><a href="#cb5-8"></a></span>
<span id="cb5-9"><a href="#cb5-9"></a> <span class="co"># Couriers and Restaurants are similar</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> ...</span>
<span id="cb5-11"><a href="#cb5-11"></a></span>
<span id="cb5-12"><a href="#cb5-12"></a></span>
<span id="cb5-13"><a href="#cb5-13"></a><span class="co"># Outside the class: helper for generating random locations in Toronto</span></span>
<span id="cb5-14"><a href="#cb5-14"></a>TORONTO_COORDS <span class="op">=</span> (<span class="fl">43.747743</span>, <span class="fl">43.691170</span>, <span class="op">-</span><span class="fl">79.633951</span>, <span class="op">-</span><span class="fl">79.176646</span>)</span>
<span id="cb5-15"><a href="#cb5-15"></a></span>
<span id="cb5-16"><a href="#cb5-16"></a></span>
<span id="cb5-17"><a href="#cb5-17"></a><span class="kw">def</span> _generate_location() <span class="op">-&gt;</span> <span class="bu">tuple</span>[<span class="bu">float</span>, <span class="bu">float</span>]:</span>
<span id="cb5-18"><a href="#cb5-18"></a> <span class="co">&quot;&quot;&quot;Return a randomly-generated location (latitude, longitude) within the Toronto bounds.</span></span>
<span id="cb5-19"><a href="#cb5-19"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> <span class="cf">return</span> (random.uniform(TORONTO_COORDS[<span class="dv">0</span>], TORONTO_COORDS[<span class="dv">1</span>]),</span>
<span id="cb5-21"><a href="#cb5-21"></a> random.uniform(TORONTO_COORDS[<span class="dv">2</span>], TORONTO_COORDS[<span class="dv">3</span>]))</span></code></pre></div>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>After completing the implementation of these two helper methods, you are ready to run the simulation! Try doing the following in the Python console:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1"></a><span class="op">&gt;&gt;&gt;</span> simulation <span class="op">=</span> FoodDeliverySimulation(datetime.datetime(<span class="dv">2020</span>, <span class="dv">11</span>, <span class="dv">30</span>), <span class="dv">7</span>, <span class="dv">4</span>, <span class="dv">100</span>, <span class="dv">50</span>)</span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="op">&gt;&gt;&gt;</span> simulation.run()</span></code></pre></div>
<p>Of course, we arent printing anything out, and the <code>FoodDeliverySimualtion.run</code> method doesnt actually return anything. You are free to insert some <code>print</code> calls to see whether events are actually being processed, but thats not the only way to see the results of the simulation.</p>
<p>Once the simulation is complete, <code>self._system</code> will have accumulated several completed orders, as a <code>list[Order]</code>. We can access these values and perform any kind of computation on them we want, just like we did all the way back in Chapter 4!</p>
<p>For example, we might ask:</p>
<ul>
<li>How many orders were delivered in total?</li>
<li>What was the average number of orders delivered per courier?</li>
<li>For a given restaurant, which menu items were most popular?</li>
<li><em>What else can you come up with?</em></li>
</ul>
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>