Applying S.O.L.I.D. Principles in .NET/C#

Applying S.O.L.I.D. Principles in .NET/C#

S - SRP - Single responsibility principle (a class should have only a single responsibility (i.e. changes to only one part of the software’s specification should be able to affect the specification of the class))

There can be only one requirement that, when changed, will cause a class to change…

More detailed:

When we build a class, we look at all the requirements for our systems,

or all the requirements that affect that class,

and we build in such a way that only one of those requirements will cause ever one class to have to change.

Nouns are classes, verbs are their functions and adjectives are their properties.

We might need to turn verbs/functions to classes as well,

As soon as we pull different small functions turned them into classes,
We get a class responsible for one thing and it can only change if only one requirement changes,

It makes a whole lot easier to change the way of our application works, because we can switch out functionality without have to modify classes but changing the class do the functionality.

put different functions into different classes.

Benefits

I can switch out components and up other components. Components are really really small.
Simply to read, simply to use, simple to understand.

O - OCP - Open/closed principle (software entities … should be open for extension, but closed for modification.)

“Software entities should be open for extension, but closed for modification.”

Once a class is done, it is done! You do not modify it.

More detailed:

A changing requirement/Once a requirement changes, you do not modify your class, you do not go in and make changes in your existing class.
You creates a new class inheriting from the old class, make changes in the new class instead.

Best part of it:

No changes for the unit tests since new class has been created, and new tests have been adding to the new class instead of changing unit tests for old class.

Meyer vs. Polymorphic

Meyer: You build a class put into your system. You build another class which inheritanced from the pervious class then put into your system.

Polymorphic: Not nessersaily have to inherited from a specfic implementation, inheriting from abstract base classes or could be implemented in a interface.

Three ways of doing OCP

  1. Inheriting the exising class, Overridding the existing class function.
  2. Inheriting Abstract class
  3. Implementing the existing interface.

Benfits

You dont change your code. No need to rework on existing unit tests.

L - LSP - Liskovs Substitution Principle (objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program)

Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.

More detailed:

A subclass should behave in such a way that it will not cause problems when used instead of the superclass.

Rules:

1. Contravariance(n.反[抗,逆]变性,逆[抗]变; 抗变性) of method arguments in sub class

if I have a class, I create a new class that inherits from that player class, I am allowed to change the input arguments term to my method in such a way that they accept a base class of the arguments that my base class accepted it

I am allowed to change the input parameters in such a way that any parameters that has ever been passed in to the parent type can be passed into the new type suite

if I have a class, then I create a new class that inherits from the old class, I am allowed to change the input arguments to my method in such a way that they accepts a base class of the arguments that my base class accepted.

TL;DR
I am allowed to change the input parameters in such a way that any parameters that has ever been passed into the parent type can be passed into the new type.

2. Covariance( n.协方差;变异数;协变性;共变性) of return types in the sub class.

the return of my class, the return value of my method is allowed to be any type that is a the same as the parent or a descendant class

I’m allowed to in return anything as long as it it’s at some point inherits from whatever the base class returned

The return value of my method in my class is allowed be any type is the same as the parent or a discendent class.

TL;DR
I am allowed to return anything as long as it somepoint inherits from whatever my base class returns.

3. No new exceptions types are allowed to be thrown, unless they are sub classes of previously used ones.

TL;DR
In your new classes, you are not allowed to introduce new exceptions.

Why?

If you introduced new exceptions, whatever code has been using in other class you base class, might be prepared to handle whatever exception that class is throwing.

If you introducing new ones, your application might be broken.

4. Preconditions cannot be strengthened in a subtype.

you cannot be expecting things that wasn’t expected in the base class.

I cannot make changes that that you’re only allowed to call this method if this property is set to this value if the base class hasn’t had that that’s limitation before

so you’re not allowed to introduce things that might cause once again any class using the base class cannot break because you’ve introduced new features or new limitations.

You can not define limitation in subclass which the base class hasnt that limitation before.

You are not allowed to introduce things that might cause any class that uses the base class cant not break because you introduces new feature or new limition.

5. Postconditions cannot be weakened in a subtype.

I’m not allowed to change the class in such a way that you cannot do things you could do before

so I can’t start throwing not implemented exception or say you’re not allowed to do this or that or whatever

because once again if I introduce a change like that code that was using the base class cannot use the new type and might actually fail.

I am not allowed to change the class in such a way that you can not do things you could do before.

I cant start throwing “not implemented exception” or you are not allowed to do this or that.

6. The history constraint(n.强制; 限制; 约束)

More Detailed:
You can not make an immutable class mutable.

You have a class that can not be changed, you have a property of the class that can not be changed in the base class.
You are not allow to change that class in such a way that you can change the value in your new class.

Because any type or any class are using the base class might be excepting that the value can not be changed.
If you introduced the possibility to changing it, you might break things.

The general guide:

You are not allowed to change a class in a way that it can break any the existing applications.

The rules to define what could break the existing application.

You should most likely to be possible to take your new class and throw it in and run it through all of your existing unit tests. And they should fail in the way that they break down.

Benfits

create new versions of classess
that will actually be able to plug in and replace the existing implementations without you breaking existing applications

I - ISP - Interface segregation principle

“Clients should not be forced to depend upon interfaces that they don’t use.”

Benfits

Breaking down interfaces in smaller pieces make them easier to implement, and offers more control over who sees what.

you get really small tiny pieces easy to implement easy to switch out it

you can have tiny little interfaces

you can have your class implement ten of them if you wanted that’s not a big problem

but the guy needing the functionality can ask for a little bit a little bitty piece of it he doesn’t need to have the whole thing.

D - DIP - Dependency inversion principle

one should “Depend upon Abstractions. Do not depend upon concretions.”

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend on details. Details should depend on abstractions.tags: [S.O.L.I.D, Object-oriented Programmming]

Without DIP

DIPwithout

With DIP
DIPwith

With DIP Alternative
DIPwith

functions

Eloquent JavaScript(The Annotated Version) - Chapter 3 - Functions

Defining a function

var square = function(x) {
return x * x;
};

console.log(square(12));
// → 144

Functions starts with keyword function, has a set of parameters and a body which contains the statements that are to be executed.

The body wrapped in braces.

A function can have multiple parameters or no parameters at all.

A return statement determines the value the function returns.

The return keyword without an expression after it will cause the function to return undefined.

Return statement - Douglas Crockford - The JavaScript Programming Language (Notes)

In JavaScript, every function will return a result, no void type in js.

  1. If there is no expression, then the return value is undefined.

  2. Except for constructors, whose default return value is this.

Parameters and scopes

The parameters to a function behave like regular variables, but their initial values are given by the caller of the function, not the code in the function itself.

An important property of functions is that the variables created inside of them, including their parameters, are local to the function.

This “localness” of variables applies only to the parameters and to variables declared with the var keyword inside the function body.

Variables declared outside of any function are called global, because they are visible throughout the program.

var x = "outside";

var f1 = function() {
// The use of var here is extremely important.
// If you declare a variable in a function using var,
// The variable will only be visible inside the function.
// In other words it’s ‘local’ to the function.
var x = "inside f1";
};
f1();
// Since we’re not inside the function anymore,
// if we print x, we’ll get “outside”.
console.log(x);
// → outside

var f2 = function() {
// We didn’t use var here, so the line below
// will actually grab the x that we declared in the
// first line of our program and set it equal to “inside f2”
// instead of its initial value, “outside”.
x = "inside f2";
};
f2();
// Since f2() changed the x outside of the function,
// printing x outside the function will give us “inside f2”.
console.log(x);
// → inside f2

Nested scope

In short, each local scope can also see all the local scopes that contain it.

The set of variables visible inside a function is determined by the place of that function in the program text.

All variables from blocks around a function’s definition are visible—meaning both those in function bodies that enclose it and those at the top level of the program.

This approach to variable visibility is called lexical scoping.

In JavaScript, functions are the only things that create a new scope not braces.

var something = 1;
{
var something = 2;
// Do stuff with variable something...
}
// Outside of the block again...

But the something inside the block refers to the same variable as the one outside the block.

The next version of JavaScript will introduce a let keyword, which works like var but creates a variable that is local to the enclosing block, not the enclosing function.

Functions as values

Function variables usually simply act as names for a specific piece of the program.
Such a variable is defined once and never changed.
This makes it easy to start confusing the function and its name.

var func = function() {};

function variable: Left side of the equation.
function value: Right side of the equation.

Declaration notation

There is a slightly shorter way to say “var square = function…”. The function keyword can also be used at the start of a statement, as in the following:

function square(x) {
return x * x;
}

This is a function declaration.

There is one subtlety with this form of function definition, however.

console.log("The future says:", future());

function future() {
return "We STILL have no flying cars.";
}

This code works, even though the function is defined below the code that uses it.

This is because function declarations are not part of the regular top-to-bottom flow of control.

They are conceptually moved to the top of their scope and can be used by all the code in that scope.

This is sometimes useful because it gives us the freedom to order code in a way that seems meaningful, without worrying about having to define all functions above their first use.

Do not define functions inside of control flow.

function example() {
function a() {} // Okay
if (something) {
function b() {} // Danger!
}
}

The call stack

Because a function has to jump back to the place of the call when it returns, the computer must remember the context from which the function was called.

In one case, console.log has to jump back to the greet function.

In the other case, it jumps back to the end of the program.

The place where the computer stores this context is the call stack.

Every time a function is called, the current context is put on top of this “stack”.

When the function returns, it removes the top context from the stack and uses it to continue execution.

If you want a more detailed explanation about call stacks and how JavaScript interprets code, this blog post is very good:
http://davidshariff.com/blog/what-is-the-execution-context-in-JavaScript/

Optional Arguments

JavaScript is extremely broad-minded about the number of arguments you pass to a function.

If you pass too many, the extra ones are ignored.

If you pass too few, the missing parameters simply get assigned the value undefined.

The downside of this is that it is possible—likely, even—that you’ll accidentally pass the wrong number of arguments to functions and no one will tell you about it.

The upside is that this behavior can be used to have a function take “optional” arguments.

Closure

function wrapValue(n) {
var localVariable = n;
return function() { return localVariable; };
}

var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2

In fact, multiple instances of the variable can be alive at the same time, which is another good illustration of the concept that local variables really are re-created for every call—different calls can’t trample on one another’s local variables.

This feature—being able to reference a specific instance of local variables in an enclosing function—is called closure. A function that “closes over” some local variables is called a closure. This behavior not only frees you from having to worry about lifetimes of variables but also allows for some creative use of function values.

Recursion

It is perfectly okay for a function to call itself, as long as it takes care not to overflow the stack. A function that calls itself is called recursive.

Growing functions

There are two more or less natural ways for functions to be introduced into programs.

  1. The first is that you find yourself writing very similar code multiple times. We want to avoid doing that since having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.

  2. The second way is that you find you need some functionality that you haven’t written yet and that sounds like it deserves its own function. You’ll start by naming the function, and you’ll then write its body. You might even start writing code that uses the function before you actually define the function itself.

How difficult it is to find a good name for a function is a good indication of how clear a concept it is that you’re trying to wrap.

Functions and side effects

Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to have both side effects and return a value.)

A pure function is a specific kind of value-producing function that not only has no side effects but also doesn’t rely on side effects from other code—for example, it doesn’t read global variables that are occasionally changed by other code.

A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn’t do anything else).

This makes it easy to reason about.

A call to such a function can be mentally substituted by its result, without changing the meaning of the code.

When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context.

Nonpure functions might return different values based on all kinds of factors and have side effects that might be hard to test and think about.

Exercises

Minimum

function min(a, b) {
if (a < b)
return a;
else
return b;
}

Recursion

 function isEven(n) {
// Base case 1, n is even.
if (n == 0)
return true;
// Base case 2, n is odd.
else if (n == 1)
return false;
// Handle negative numbers by just
// making them positive.
else if (n < 0)
return isEven(-n);
// Recursive case.
else
return isEven(n - 2);
}

Bean counting

function countBs(sampleString) {
var counted = 0;
for (var i = 0; i < sampleString.length; i++)
if (sampleString.charAt(i) == 'B')
counted += 1;
return counted;
}

function countChar(sampleString, searchForChar) {
var counted = 0;
for (var i = 0; i < sampleString.length; i++ )
if (sampleString.charAt(i) == searchForChar)
counted += 1;
return counted;
}

function countBs(sampleString) {
return countChar(sampleString, "B");
}

octopus

Eloquent JavaScript(The Annotated Version) - Chapter 2 - Program Structure

Expressions and statements

an expression is a sentence fragment, a JavaScript statement is a full sentence in a human language.

“Expression” is:

  1. simply values
  2. made some values and then applied operatros to them to get new values.
  3. An expression between parentheses
  4. a binary operator applied to two expressions
  5. a unary operator applied to one expression

“Statement” is:

  1. an expression -> a sentence fragment
    a JavaScript statement -> a full sentence in a human language.
  2. A program is simply a list of statements.

Recommending that every statement needs a semicolon will always be terminated by one.

Variables

The special word (keyword) var indicates that this sentence is going to define a variable. It is followed by the name of the variable and, if we want to immediately give it a value, by an = operator and an expression.

Naming Variables:

  1. Variable names can be any word that isn’t a reserved word (such as var).
  2. Digits can also be part of variable names, but the name must not start with a digit.
  3. Variable names no punctuation, except for $ and _.

Variables vs Values:

  1. Variables do not contain values, variables refer to values.
  2. two variables can refer to the same values
  3. A program can access only the values that it still has a hold on via variables it contains.

you create a variable to grasp the value or you change the existing variable to grasp the value.

When you define a variable without giving it a value, If you ask for the value of an empty variable, you’ll get the value undefined.

Variable names are also case sensitive. So newVariable is not the same as NewVariable.

Keywords and reserved words

a number of words “reserved for use” in JavaScript world. Not allowed to be used as variable names.

break case catch class const continue debugger
default delete do else enum export extends false
finally for function if implements import in
instanceof interface let new null package private
protected public return static super switch this
throw true try typeof var void while with yield

Reserved words in Douglas Crockford - The JavaScript Programming Language (Notes)

The environment

The collection of variables and their values that exist at a given time is called the environment.

Functions

Function is a type - a piece of program wrapped in a value.

  1. Executing a function is called invoking, calling, or applying it.
  2. You can call a function by putting parentheses after an expression that produces a function value.
  3. Usually you’ll directly use the name of the variable that holds the funciton.
  4. The values between the parentheses are given to the program inside the function.
    Values given to functions are called arguments.

The console.log function

Use console.log to output values.
why contain period characters?
We will find out exactly what this means in Chapter 4.

Return values

When a function produces a value, it is said to return that value.
Anything that produces a value is an expression in JavaScript, which means function calls can be used within larger expressions.

The next chapter(chapter 3) explains how to write your own functions.

Prompt and Confirm

confirm("Shall we, then?");
prompt("Tell me everything you know.", "...");

Control flow

More than one statement are executed from top to bottom.

Conditional execution

if (...)
....
else if (...)
....
else
....

While and do loops

while (...) {
...
}
do {
...
}
while (...)

Indenting Code

To make the structure of the code stand out

For loops

var result = 1;
for (var counter = 0; counter < 10; counter = counter + 1)
{
result = result * 2;
}

console.log(result);

Breaking Out of a Loop

break and continue

Updating variables succinctly

+=
*=
-=
++
--

Dispatching on a value with switch

switch (prompt("What is the weather like?")) {
case "rainy":
console.log("Remember to bring an umbrella.");
break;
case "sunny":
console.log("Dress lightly.");
case "cloudy":
console.log("Go outside.");
break;
default:
console.log("Unknown weather type!");
break;
}

Capitalization

Camel Case and Pascal Case in Chapter 6.

Comments

// slash slash

/*
slash star
star slash
*/

Exercises

1.Write a loop that makes seven calls to console.log to output the following triangle:

#
##
###
####
#####
######
#######
for (var line = "#"; line.length < 8; line += "#")
console.log(line);

My solution

for(i = 1; i < 8; i++)
{
var j = 0;
var wall = "";

while (j < i)
{
wall += "#";
j++
}
console.log(wall);
console.log("\n");
}

2.FizzBuzz

Write a program that uses console.log to print all the numbers from 1 to 100, with two exceptions. For numbers divisible by 3, print “Fizz” instead of the number, and for numbers divisible by 5 (and not 3), print “Buzz” instead.

When you have that working, modify your program to print “FizzBuzz”, for numbers that are divisible by both 3 and 5 (and still print “Fizz” or “Buzz” for numbers divisible by only one of those).

(This is actually an interview question that has been claimed to weed out a significant percentage of programmer candidates. So if you solved it, you’re now allowed to feel good about yourself.)

for (var n = 1; n <= 100; n++) {
var output = "";
if (n % 3 == 0)
output += "Fizz";
if (n % 5 == 0)
output += "Buzz";
console.log(output || n);
}

Paul Irish’s solution

for (var i = 1; i <= 100; i++) {
  var f = i % 3 == 0, b = i % 5 == 0;
  console.log(f ? b ? "FizzBuzz" : "Fizz" : b ? "Buzz" : i);
}

My solution

for (i = 1; i < 101; i++) {
if (i%3 == 0 && i%5 == 0) {
console.log("FizzBuzz");
continue;
}

if (i%3 == 0) {
console.log("Fizz");
}
else if (i%5 == 0) {
console.log("Buzz");
}
else {
console.log(i);
}
}

3.Write a program that creates a string that represents an 8×8 grid, using newline characters to separate lines. At each position of the grid there is either a space or a “#” character. The characters should form a chess board.

Passing this string to console.log should show something like this:

 # # # #
# # # #
 # # # #
# # # #
 # # # #
# # # #
 # # # #
# # # #

When you have a program that generates this pattern, define a variable size = 8 and change the program so that it works for any size, outputting a grid of the given width and height.

var size = 8;

var board = "";

for (var y = 0; y < size; y++) {
for (var x = 0; x < size; x++) {
if ((x + y) % 2 == 0)
board += " ";
else
board += "#";
}
board += "\n";
}

console.log(board);

Douglas Crockford -  Advanced JavaScript

Douglas Crockford - Advanced JavaScript

*Inheritance

*Modules

*Debugging

*Effiency

*JSON

Inheritance

  • Inheritance is object-oriented code reuse.

  • Two Schools:

    • Classical

    • Prototypal - which JavaScript is about the only instance.

Classical Inheritance

  • Objects are instances of classes.

  • A class inherits from another class.

Prototypal Inheritance

  • Class-free - No classes

  • Objects inherit from objects.

  • An object contains a secret link to another object from which it inherits properties.

  • Mozilla calls it proto .

var newObject = object(oldObject);

var oldObject = {
firstMethod: function () { ... },
secondMethod: function () { ... }
};

var newObject = object(oldObject);

newObject.thirdMethod = function () { ... };

var myDoppelganger = object(newObject);

myDoppelganger.firstMethod();

Prototypal Inheritance

The way it works is if we try to access a property of an object,

and that object lacks that property, then we follow the linkage

to the object that it inheritance from to see if it has it,

if it does, than it provides the access.

If the access fails, then we can continue the search from this one.

Alternative, it stops at Object.prototype because Object.prototype is the end of the chain.

  • If an object has a foo property, then the chain will not be consulted when accessing member foo.

    newObject.foo newObject[‘foo’]

  • If access of a member of newObject fails, then search for the member in oldObject.

  • If that fails, then search for the member in Object.prototype.

newObject -> oldObject

Prototypal Inheritance

  • Changes in oldObject may be immediately visible in newObject.

  • Changes to newObject have no effect on oldObject.

Prototypal Inheritance

oldObject can be the prototype for an unlimited number of objects which will all inherit its properities.

Prototypal Inheritance

  • newObject can be the prototype for an unlimited number of even newer objects.

  • There is no limit to the length of the chain (except common sense).

Augmentation

  • Using the object function, we can quickly produce new objects that have the same state and behavior as exising objects.

  • We can then augment each of the instances by assigning new methods and members.

Pseudoclassical

  • A prototypal inheritance language should have an operator like the object
    function, which makes a new object using an existing object as its prototype.

  • JavaScript instead uses operators that look classical, but behave prototypally.

  • They tried to have it both ways.

Pseudoclassical

Three mechanisms:

  • Constructor functions.

  • The new operator.

  • The prototype member of functions.

new operator

function Constructor() {
this.member = initialiser;
return this; // optinonal
}

Constructor.prototype.firstMethod =
function (a, b) { ... };

Constructor.prototype.secondMethod =
function (c) { ... };

var newobject = new Constructor() ;

Constructor

  • When functions are designed to be used with new, they are called
    constructors.

  • Constructors are used to make objects of a type or class/seudoclass.

  • JavaScript’s notation can get a little strange because it is trying to look like the old familiar classical pattern, while also trying to be something really different.

new operator

new Constructor() returns a new object with a link to
Constructor.prototype.

var newObject = new Constructor();

new operator

The Constructor() function is passed the new object in the this variable.

This allows the Constructor function to customize the new object.

Warning

  • The new operator is required when calling a Constructor.

  • If new is ommited, the global object is clobbered by the constructor, this would be the global object.
    and then the global object is returned instead of a new instance.

Prototype

  • When a function object is created, it is given a prototype member which is an object containing a constructor member which is a reference to the function object.

  • You can add other members to function’s prototype. These members will be linked into objects that are produced by calling the function with the new operator.

  • This allows for adding constants and methods to every object produced, without the objects having to be enlarged to contain them.

  • Differential Inheritance.

method method

Function.prototype.method = 
function (name, func) {
this.prototype[name] = func;
return this;
}

Constructor.

method('first_method', 
function (a, b) {...}).
method('second_method',
function (c) {...});

Pseudoclassical Inheritance

  • Classical Inheritance can be simulated by assigning an object created by one constructor to the prototype member of another.

  • This does not work exactly like the classical model.

function BiggerConstructor() {};

BiggerConstructor.prototype = new MyConstructor();

Example

function Gizmo(id) {
this.id = id;
}

Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};

Inheritance

  • If we replace the original prototype object with an instance of an object of another class, then we can inherit another class’s stuff.
function Hoozit(id) {
this.id = id;
}

Hoozit.prototype = new Gizmo();

Hoozit.prototype.test = function (id) {
return this.id === id;
};

object function

A prototypal inheritance language should have an operator like the object function, which makes a new object using an existing object as its prototype.

object function

function object(o) {
function F() {}
F.prototype = o;
return new F();
}

Public Method

  • A Public Method is a function that uses this to access its object.
  • A Public Method can be reused with many “classes”.

Public Methods

function (string) {
return this.member + string;
}

  • We can put this function in any object at it works.
  • Public methods work extremely well with prototypal inheritance and with pseudoclassical Inheritance.

Singletons

  • There is no need to produce a class-like constructor for an object that will have exactly one instance.

  • Instead, simple use an object literal.

Singletons

var singleton = {
firstMethod: function (a, b) {
...
},
secondMethod: function (c) {
...
}
};

Singletons

  • The methods of a singleton can enjoy access to shared private data and private methods.

Functions
Functions are used as

  • Functions
  • Methods
  • Constructors
  • Classes
  • Modules

Module

  • Variables defined in a module are only visible in the module.
  • Functions have scope.
  • Variables defined in a function only visible in the function.
  • Functions can be used a module containers.

Global variables are evil

  • Functions within an application can clobber each other.

  • Cooperating applications can clobber each other.

  • Use of the global namespace must be minimized.

Another way to write Singletons

var singleton = function () {
var privateVariable;
function privateFunction(x) {
... privateVariable ...
}

return {
firstMethod: function (a, b) {
... privateVariable ...
},
secondMethod: function (c) {
...privateFunction() ...
}
};
}();

Applications are Singletons

YAHOO.MyProperty = function () {
var privateVariable;
function privateFunction(x) {
...privateVariable...
}

return {
firstMethod: function (a, b) {
...privateVariable...
},
secondMethod: function (c) {
...privateFunction()...
}
};
}();

Privileged Method

  • A Privileged Method is a function that has access to secret information.

  • A Privileged Method has access to private variables and private methods.

  • A Privileged Method obtains its secret information through closure.

Power Constructor

  • Put the singleton module pattern in constructor function, and we have a power constructor pattern.
  1. Make a new object somehow.
  2. Augment it.
  3. Return it.
function powerConstructor() {
var that = object(oldObject),
privateVariable;
function privateFunction(x) {}

that.firstMethod = function (a, b) {
... privateVariable ...
};

that.secondMethod = function (c) {
... privateFunction() ...
};

return that;
}

Power Constructor

  • Public methods (from the prototype)
    var that = object(my_base);

  • Private variables (var)

  • Private methods (inner functions)

  • Privileged methodss (that…)

  • No need to use new
    myObject = power_constructor();

Parasitic Inheritance

A power constructor calls another constructor, takes the result, augments it, and return it as though it did all the work.

function symbol(s, p) {
return {
id: s,
lbp: p,
value: s
};
}

function delim(s) {
return symbol(s, 0);
}

function stmt(s, f) {
var x = delim(s);
x.identifier = true;
x.reserved = true;
x.fud = f;
return x;
}

function blockstmt(s, f) {
var x = stmt(s, f);
x.block = true;
return x;
}

Pseudoclassical Inheritance

function Gizmo(id) {
this.id = id;
}

Gizmo.prototype.toString = function () {
return "gizmo " + this.id;
};

function Hoozit(id) {
this.id = id;
}

Hoozit.prototype = new Gizmo();

Hoozit.prototype.test = function (id) {
return this.id === id;
}

Parasitic Inheritance

function gizmo(id) {
return {
id: id,
toString: function () {
return "gizmo " + this.id;
}
};
}

function hoozit(id) {
var that = gizmo(id);

that.test = function (testid) {
return testid === this.id;
};

return that;
}

Secrets

function gizmo(id) {
return {
toString: function () {
return "gizmo " + id;
}
};
}

function hoozit(id) {
var that = gizmo(id);

that.test = function (testid) {
return testid === this.id;
};

return that;
}

Shared Secrets

function gizmo(id, secret) {
secret = secret || {};
secret.id = id;
return {
toString: function () {
return "gizmo " + secret.id;
};
};
}

function hoozit(id) {
var secret = {}, /*final*/
that = gizmo(id, secret);

that.test = function (testid) {
return testid === secret.id;
};

return that;
}

Super Methods

function boozit(id) {
var secret = {},
that = gizmo(id, secret),
super_toString = that.toString;

that.test = function (testid) {
return testid === secret.id;
};

that.toString = function () {
return super_toString.apply(that, []);
};

return that;
}

Inheritance Patterns

  • Prototypal Inheritance works really well with public methods.

  • Parasitic Inheritance works really well with privileged and private and public methods.

  • Pseudoclassical Inheritance for elderly programmers who are old and set in their ways.

Working with the Grain

  • Pseudoclassical patterns are less effective that prototypal patterns or parasitic patterns.

  • Formal classes are not needed for reuse or extension.

  • Be shallow. Deep hierarchies are not effective.

later method

The later method causes a method on the object to be invoked in the future.

my_object.later(1000, “erase”, true);

later method

Object.prototype.later = 
function (msec, method) {
var that = this,
args = Array.prototype.slice.apply(arguments, [2]);

if (typeof method === 'string') {
method = that[method];
}

setTimeout(function () {
method.apply(that, args);
}, msec);

return that;
};

Multiples

  • when assigning functions in a loop, be aware that all of the functions are bound to the same closure.
  • This can be avoided by using a factor function to produce unique bindings.

Multiples

for (i ...) {
var div_id = divs[i].id;
divs[i].onmouseover = function () {
show_element_id(div_id);
};
}

for (i ...) {
var div_id = divs[i].id;
divs[i].onmouseover = functioin (id) {
return function () {
show_element_id(id);
};
}(div_id);
}