387 lines
35 KiB
HTML
387 lines
35 KiB
HTML
|
<!DOCTYPE HTML>
|
|||
|
<html lang="en" class="sidebar-visible no-js light">
|
|||
|
<head>
|
|||
|
<!-- Book generated using mdBook -->
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>Characteristics of Object-Oriented Languages - The Rust Programming Language</title>
|
|||
|
|
|||
|
|
|||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|||
|
<meta name="description" content="">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|||
|
<meta name="theme-color" content="#ffffff" />
|
|||
|
|
|||
|
<link rel="shortcut icon" href="favicon.png">
|
|||
|
<link rel="stylesheet" href="css/variables.css">
|
|||
|
<link rel="stylesheet" href="css/general.css">
|
|||
|
<link rel="stylesheet" href="css/chrome.css">
|
|||
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|||
|
|
|||
|
<!-- Fonts -->
|
|||
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|||
|
<link href="googleFonts/css.css" rel="stylesheet" type="text/css">
|
|||
|
|
|||
|
<!-- Highlight.js Stylesheets -->
|
|||
|
<link rel="stylesheet" href="highlight.css">
|
|||
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|||
|
|
|||
|
<!-- Custom theme stylesheets -->
|
|||
|
|
|||
|
<link rel="stylesheet" href="ferris.css">
|
|||
|
|
|||
|
<link rel="stylesheet" href="theme/2018-edition.css">
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<!-- Provide site root to javascript -->
|
|||
|
<script type="text/javascript">
|
|||
|
var path_to_root = "";
|
|||
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|||
|
<script type="text/javascript">
|
|||
|
try {
|
|||
|
var theme = localStorage.getItem('mdbook-theme');
|
|||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|||
|
|
|||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|||
|
}
|
|||
|
|
|||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|||
|
}
|
|||
|
} catch (e) { }
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|||
|
<script type="text/javascript">
|
|||
|
var theme;
|
|||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|||
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|||
|
var html = document.querySelector('html');
|
|||
|
html.classList.remove('no-js')
|
|||
|
html.classList.remove('light')
|
|||
|
html.classList.add(theme);
|
|||
|
html.classList.add('js');
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|||
|
<script type="text/javascript">
|
|||
|
var html = document.querySelector('html');
|
|||
|
var sidebar = 'hidden';
|
|||
|
if (document.body.clientWidth >= 1080) {
|
|||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|||
|
sidebar = sidebar || 'visible';
|
|||
|
}
|
|||
|
html.classList.remove('sidebar-visible');
|
|||
|
html.classList.add("sidebar-" + sidebar);
|
|||
|
</script>
|
|||
|
|
|||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|||
|
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
|||
|
<ol class="chapter"><li class="expanded affix "><a href="title-page.html">The Rust Programming Language</a></li><li class="expanded affix "><a href="foreword.html">Foreword</a></li><li class="expanded affix "><a href="ch00-00-introduction.html">Introduction</a></li><li class="expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Installation</a></li><li class="expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Programming a Guessing Game</a></li><li class="expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Common Programming Concepts</a></li><li><ol class="section"><li class="expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Variables and Mutability</a></li><li class="expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Data Types</a></li><li class="expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Functions</a></li><li class="expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Comments</a></li><li class="expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Control Flow</a></li></ol></li><li class="expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Understanding Ownership</a></li><li><ol class="section"><li class="expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> What is Ownership?</a></li><li class="expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> References and Borrowing</a></li><li class="expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> The Slice Type</a></li></ol></li><li class="expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Using Structs to Structure Related Data</a></li><li><ol class="section"><li class="expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Defining and Instantiating Structs</a></li><li class="expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> An Example Program Using Structs</a></li><li class="expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Method Syntax</a></li></ol></li><li class="expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Enums and Pattern Matching</a></li><li><ol class="section"><li class="expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Defining an Enum</a></li><li class="expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> The match Control Flow Operator</a></li><li class="expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Concise Control Flow with if let</a></li></ol></li><li class="expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Managing Growing Projects with Packages, Crates, and Modules</a></li><li><ol class="section"><li class="expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Packages and Crates</a></li><li class="expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Defining Modules to Control Scope and Privacy</a></li><li class="expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><
|
|||
|
</div>
|
|||
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|||
|
</nav>
|
|||
|
|
|||
|
<div id="page-wrapper" class="page-wrapper">
|
|||
|
|
|||
|
<div class="page">
|
|||
|
|
|||
|
<div id="menu-bar" class="menu-bar">
|
|||
|
<div id="menu-bar-sticky-container">
|
|||
|
<div class="left-buttons">
|
|||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|||
|
<i class="fa fa-bars"></i>
|
|||
|
</button>
|
|||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|||
|
<i class="fa fa-paint-brush"></i>
|
|||
|
</button>
|
|||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|||
|
<i class="fa fa-search"></i>
|
|||
|
</button>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<h1 class="menu-title">The Rust Programming Language</h1>
|
|||
|
|
|||
|
<div class="right-buttons">
|
|||
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|||
|
<i id="print-button" class="fa fa-print"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<div id="search-wrapper" class="hidden">
|
|||
|
<form id="searchbar-outer" class="searchbar-outer">
|
|||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|||
|
</form>
|
|||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|||
|
<ul id="searchresults">
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|||
|
<script type="text/javascript">
|
|||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<div id="content" class="content">
|
|||
|
<main>
|
|||
|
<h2><a class="header" href="#characteristics-of-object-oriented-languages" id="characteristics-of-object-oriented-languages">Characteristics of Object-Oriented Languages</a></h2>
|
|||
|
<p>There is no consensus in the programming community about what features a
|
|||
|
language must have to be considered object oriented. Rust is influenced by many
|
|||
|
programming paradigms, including OOP; for example, we explored the features
|
|||
|
that came from functional programming in Chapter 13. Arguably, OOP languages
|
|||
|
share certain common characteristics, namely objects, encapsulation, and
|
|||
|
inheritance. Let’s look at what each of those characteristics means and whether
|
|||
|
Rust supports it.</p>
|
|||
|
<h3><a class="header" href="#objects-contain-data-and-behavior" id="objects-contain-data-and-behavior">Objects Contain Data and Behavior</a></h3>
|
|||
|
<p>The book <em>Design Patterns: Elements of Reusable Object-Oriented Software</em> by
|
|||
|
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley
|
|||
|
Professional, 1994) colloquially referred to as <em>The Gang of Four</em> book, is a
|
|||
|
catalog of object-oriented design patterns. It defines OOP this way:</p>
|
|||
|
<blockquote>
|
|||
|
<p>Object-oriented programs are made up of objects. An <em>object</em> packages both
|
|||
|
data and the procedures that operate on that data. The procedures are
|
|||
|
typically called <em>methods</em> or <em>operations</em>.</p>
|
|||
|
</blockquote>
|
|||
|
<p>Using this definition, Rust is object oriented: structs and enums have data,
|
|||
|
and <code>impl</code> blocks provide methods on structs and enums. Even though structs and
|
|||
|
enums with methods aren’t <em>called</em> objects, they provide the same
|
|||
|
functionality, according to the Gang of Four’s definition of objects.</p>
|
|||
|
<h3><a class="header" href="#encapsulation-that-hides-implementation-details" id="encapsulation-that-hides-implementation-details">Encapsulation that Hides Implementation Details</a></h3>
|
|||
|
<p>Another aspect commonly associated with OOP is the idea of <em>encapsulation</em>,
|
|||
|
which means that the implementation details of an object aren’t accessible to
|
|||
|
code using that object. Therefore, the only way to interact with an object is
|
|||
|
through its public API; code using the object shouldn’t be able to reach into
|
|||
|
the object’s internals and change data or behavior directly. This enables the
|
|||
|
programmer to change and refactor an object’s internals without needing to
|
|||
|
change the code that uses the object.</p>
|
|||
|
<p>We discussed how to control encapsulation in Chapter 7: we can use the <code>pub</code>
|
|||
|
keyword to decide which modules, types, functions, and methods in our code
|
|||
|
should be public, and by default everything else is private. For example, we
|
|||
|
can define a struct <code>AveragedCollection</code> that has a field containing a vector
|
|||
|
of <code>i32</code> values. The struct can also have a field that contains the average of
|
|||
|
the values in the vector, meaning the average doesn’t have to be computed
|
|||
|
on demand whenever anyone needs it. In other words, <code>AveragedCollection</code> will
|
|||
|
cache the calculated average for us. Listing 17-1 has the definition of the
|
|||
|
<code>AveragedCollection</code> struct:</p>
|
|||
|
<p><span class="filename">Filename: src/lib.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>pub struct AveragedCollection {
|
|||
|
list: Vec<i32>,
|
|||
|
average: f64,
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 17-1: An <code>AveragedCollection</code> struct that
|
|||
|
maintains a list of integers and the average of the items in the
|
|||
|
collection</span></p>
|
|||
|
<p>The struct is marked <code>pub</code> so that other code can use it, but the fields within
|
|||
|
the struct remain private. This is important in this case because we want to
|
|||
|
ensure that whenever a value is added or removed from the list, the average is
|
|||
|
also updated. We do this by implementing <code>add</code>, <code>remove</code>, and <code>average</code> methods
|
|||
|
on the struct, as shown in Listing 17-2:</p>
|
|||
|
<p><span class="filename">Filename: src/lib.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">pub struct AveragedCollection {
|
|||
|
</span><span class="boring"> list: Vec<i32>,
|
|||
|
</span><span class="boring"> average: f64,
|
|||
|
</span><span class="boring">}
|
|||
|
</span>impl AveragedCollection {
|
|||
|
pub fn add(&mut self, value: i32) {
|
|||
|
self.list.push(value);
|
|||
|
self.update_average();
|
|||
|
}
|
|||
|
|
|||
|
pub fn remove(&mut self) -> Option<i32> {
|
|||
|
let result = self.list.pop();
|
|||
|
match result {
|
|||
|
Some(value) => {
|
|||
|
self.update_average();
|
|||
|
Some(value)
|
|||
|
},
|
|||
|
None => None,
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pub fn average(&self) -> f64 {
|
|||
|
self.average
|
|||
|
}
|
|||
|
|
|||
|
fn update_average(&mut self) {
|
|||
|
let total: i32 = self.list.iter().sum();
|
|||
|
self.average = total as f64 / self.list.len() as f64;
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 17-2: Implementations of the public methods
|
|||
|
<code>add</code>, <code>remove</code>, and <code>average</code> on <code>AveragedCollection</code></span></p>
|
|||
|
<p>The public methods <code>add</code>, <code>remove</code>, and <code>average</code> are the only ways to access
|
|||
|
or modify data in an instance of <code>AveragedCollection</code>. When an item is added
|
|||
|
to <code>list</code> using the <code>add</code> method or removed using the <code>remove</code> method, the
|
|||
|
implementations of each call the private <code>update_average</code> method that handles
|
|||
|
updating the <code>average</code> field as well.</p>
|
|||
|
<p>We leave the <code>list</code> and <code>average</code> fields private so there is no way for
|
|||
|
external code to add or remove items to the <code>list</code> field directly; otherwise,
|
|||
|
the <code>average</code> field might become out of sync when the <code>list</code> changes. The
|
|||
|
<code>average</code> method returns the value in the <code>average</code> field, allowing external
|
|||
|
code to read the <code>average</code> but not modify it.</p>
|
|||
|
<p>Because we’ve encapsulated the implementation details of the struct
|
|||
|
<code>AveragedCollection</code>, we can easily change aspects, such as the data structure,
|
|||
|
in the future. For instance, we could use a <code>HashSet<i32></code> instead of a
|
|||
|
<code>Vec<i32></code> for the <code>list</code> field. As long as the signatures of the <code>add</code>,
|
|||
|
<code>remove</code>, and <code>average</code> public methods stay the same, code using
|
|||
|
<code>AveragedCollection</code> wouldn’t need to change. If we made <code>list</code> public instead,
|
|||
|
this wouldn’t necessarily be the case: <code>HashSet<i32></code> and <code>Vec<i32></code> have
|
|||
|
different methods for adding and removing items, so the external code would
|
|||
|
likely have to change if it were modifying <code>list</code> directly.</p>
|
|||
|
<p>If encapsulation is a required aspect for a language to be considered object
|
|||
|
oriented, then Rust meets that requirement. The option to use <code>pub</code> or not for
|
|||
|
different parts of code enables encapsulation of implementation details.</p>
|
|||
|
<h3><a class="header" href="#inheritance-as-a-type-system-and-as-code-sharing" id="inheritance-as-a-type-system-and-as-code-sharing">Inheritance as a Type System and as Code Sharing</a></h3>
|
|||
|
<p><em>Inheritance</em> is a mechanism whereby an object can inherit from another
|
|||
|
object’s definition, thus gaining the parent object’s data and behavior without
|
|||
|
you having to define them again.</p>
|
|||
|
<p>If a language must have inheritance to be an object-oriented language, then
|
|||
|
Rust is not one. There is no way to define a struct that inherits the parent
|
|||
|
struct’s fields and method implementations. However, if you’re used to having
|
|||
|
inheritance in your programming toolbox, you can use other solutions in Rust,
|
|||
|
depending on your reason for reaching for inheritance in the first place.</p>
|
|||
|
<p>You choose inheritance for two main reasons. One is for reuse of code: you can
|
|||
|
implement particular behavior for one type, and inheritance enables you to
|
|||
|
reuse that implementation for a different type. You can share Rust code using
|
|||
|
default trait method implementations instead, which you saw in Listing 10-14
|
|||
|
when we added a default implementation of the <code>summarize</code> method on the
|
|||
|
<code>Summary</code> trait. Any type implementing the <code>Summary</code> trait would have the
|
|||
|
<code>summarize</code> method available on it without any further code. This is similar to
|
|||
|
a parent class having an implementation of a method and an inheriting child
|
|||
|
class also having the implementation of the method. We can also override the
|
|||
|
default implementation of the <code>summarize</code> method when we implement the
|
|||
|
<code>Summary</code> trait, which is similar to a child class overriding the
|
|||
|
implementation of a method inherited from a parent class.</p>
|
|||
|
<p>The other reason to use inheritance relates to the type system: to enable a
|
|||
|
child type to be used in the same places as the parent type. This is also
|
|||
|
called <em>polymorphism</em>, which means that you can substitute multiple objects for
|
|||
|
each other at runtime if they share certain characteristics.</p>
|
|||
|
<blockquote>
|
|||
|
<h3><a class="header" href="#polymorphism" id="polymorphism">Polymorphism</a></h3>
|
|||
|
<p>To many people, polymorphism is synonymous with inheritance. But it’s
|
|||
|
actually a more general concept that refers to code that can work with data
|
|||
|
of multiple types. For inheritance, those types are generally subclasses.</p>
|
|||
|
<p>Rust instead uses generics to abstract over different possible types and
|
|||
|
trait bounds to impose constraints on what those types must provide. This is
|
|||
|
sometimes called <em>bounded parametric polymorphism</em>.</p>
|
|||
|
</blockquote>
|
|||
|
<p>Inheritance has recently fallen out of favor as a programming design solution
|
|||
|
in many programming languages because it’s often at risk of sharing more code
|
|||
|
than necessary. Subclasses shouldn’t always share all characteristics of their
|
|||
|
parent class but will do so with inheritance. This can make a program’s design
|
|||
|
less flexible. It also introduces the possibility of calling methods on
|
|||
|
subclasses that don’t make sense or that cause errors because the methods don’t
|
|||
|
apply to the subclass. In addition, some languages will only allow a subclass
|
|||
|
to inherit from one class, further restricting the flexibility of a program’s
|
|||
|
design.</p>
|
|||
|
<p>For these reasons, Rust takes a different approach, using trait objects instead
|
|||
|
of inheritance. Let’s look at how trait objects enable polymorphism in Rust.</p>
|
|||
|
|
|||
|
</main>
|
|||
|
|
|||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|||
|
<!-- Mobile navigation buttons -->
|
|||
|
|
|||
|
<a rel="prev" href="ch17-00-oop.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a rel="next" href="ch17-02-trait-objects.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
<div style="clear: both"></div>
|
|||
|
</nav>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|||
|
|
|||
|
<a href="ch17-00-oop.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a href="ch17-02-trait-objects.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</nav>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script type="text/javascript">
|
|||
|
window.playpen_copyable = true;
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
|
|||
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
<!-- Custom JS scripts -->
|
|||
|
|
|||
|
<script type="text/javascript" src="ferris.js"></script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</body>
|
|||
|
</html>
|