Skip to content

Instantly share code, notes, and snippets.

@supersym
Last active December 11, 2015 01:59
Show Gist options
  • Save supersym/4527142 to your computer and use it in GitHub Desktop.
Save supersym/4527142 to your computer and use it in GitHub Desktop.

Factory Method Pattern

P: You don’t know what kind of object you will need until runtime. S: Use the Factory Method pattern and choose the object to be generated dynamically.

Say that you need to load a file into an editor but you don’t know its format until the user chooses the file. A class using the Factory Method pattern can serve up different parsers depending on the file’s extension.

class HTMLParser
    constructor: ->
		@type = "HTML parser"
class MarkdownParser
	constructor: ->
		@type = "Markdown parser"
class JSONParser
	constructor: ->
		@type = "JSON parser"

class ParserFactory
	makeParser: (filename) ->
		matches = filename.match /\.(\w*)$/
		extension = matches[1]
		switch extension
			when "html" then new HTMLParser
			when "htm" then new HTMLParser
			when "markdown" then new MarkdownParser
			when "md" then new MarkdownParser
			when "json" then new JSONParser

factory = new ParserFactory

factory.makeParser("example.html").type # => "HTML parser"

factory.makeParser("example.md").type # => "Markdown parser"

factory.makeParser("example.json").type # => "JSON parser"

Constructor arguments passed to super-type

Method 1 (potentially troublesome)

class List extends Array
    constructor: ->
        @push arguments...

    toString: ->
        @join('-')

list = new List(1, 2)
list.push(3)
list.toString()

# out => '1-2-3'

In general, this would work without additional code; the parent constructor is used unless expressly overridden:

class A
  constructor: ->
    console.log arg for arg in arguments

class B extends A

new B('foo') # output: 'foo'

And the problem isn't that Array doesn't have a constructor method:

coffee> Array.constructor
[Function: Function]

Option 2 (better handling of lists)

The problem is that Array is just plain weird. While arrays are "just objects" in principle, in practice they're stored differently. So when you try to apply that constructor to an object that isn't an array (even if it passes the instanceof Array test), it doesn't work.

So, you can use the option 1 solution, but then you may run into other problems down the road (especially if you pass a List to something that expects a true array). For that reason, I'd recommend implementing List as a wrapper around an array instance, rather than trying to use inheritance from a native object type.

While we're on the subject, one very important clarification: When you use super by itself, that does pass all arguments! This behavior is borrowed from Ruby. So

class B extends A
  constructor: ->
    super

will pass along all arguments to A's constructor, while

class B extends A
  constructor: ->
    super()

will invoke A's constructor with no arguments.

Constructor overloads

class Vector
  constructor:(@x=0,@y=0) ->
      if typeof @x is "object"
        vector=@x
        @x=vector.x
        @y=vector.y

###
test start
###
v=new Vector()
console.log v.x,v.y
v=new Vector(1,1)
console.log v.x,v.y
v=new Vector {x:1,y:1}
console.log v.x,v.y
###
test end
###

Code Reuse on Client and Server

Problem

You have created some functionality in CoffeeScript that you wish to use on the client with a web browser and on the server with Node.js.

# simpleMath.coffee
# these methods are private
add = (a, b) ->
21a + b

subtract = (a, b) ->
	a - b

square = (x) ->
	x * x

# create a namespace to export our public methods
SimpleMath = exports? and exports or @SimpleMath = {}

# items attached to our namespace are available in Node.js as well as client browsers
class SimpleMath.Calculator
	add: add
	subtract: subtract
	square: square

Some points there seems to be a conflict in paradigms emergin.

The whole concept of enum is just useless in dynamic languages as is tuple, typed list, map and lots of other stuff, and Javascript (Coffeescript) is dynamic. While working with dynamic language you just have to forget about type checking and use the existing more general constructs to solve your problem. Use arrays instead of lists and tuples, use objects instead of maps and enums and just trust the type of value passed to the function, but heavily unit-test your code. For better or worse (for worse IMO) that's just how a work is done here.

In your case I would recommend just storing your values in a singleton object, like so:

HTTPStatusCodes = 
  ok : 200
  badRequest : 400
  unauthorized : 401

and accessing it like so:

class SomeService
  okCode: ->
    HTTPStatusCodes.ok
  failureCodes: ->
    code for key, code of HTTPStatusCodes when code >= 400

Replacing Substrings

You need to replace a portion of a string with another value. Use the JavaScript replace method. replace matches with the given string, and returns the edited string.

The first version takes 2 arguments: pattern and string replacement

"JavaScript is my favorite!".replace /Java/, "Coffee"
# => 'CoffeeScript is my favorite!'

"foo bar baz".replace /ba./, "foo"
# => 'foo foo baz'

"foo bar baz".replace /ba./g, "foo"
# => 'foo foo foo'

The second version takes 2 arguments: pattern and callback function

"CoffeeScript is my favorite!".replace /(\w+)/g, (match) ->
  match.toUpperCase()
# => 'COFFEESCRIPT IS MY FAVORITE!'
The callback function is invoked for each match, and the match value is passed as the argument to the callback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment