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"
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]
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.
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
###