Skip to content

Commit 9cc5061

Browse files
committed
refactor class/Object suite and add Pattern
1 parent e48e530 commit 9cc5061

File tree

2 files changed

+214
-217
lines changed

2 files changed

+214
-217
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package intro
2+
3+
import org.scalatest.FunSuite
4+
5+
/**
6+
* Workshop exercise showing classes and objects
7+
*/
8+
class ClassPatternSuite extends FunSuite {
9+
10+
//Traits are ALMOST like 'interfaces', they can't be instantiated directly
11+
trait Language {
12+
def isFunctional: Boolean
13+
}
14+
15+
//This is how you can write a class, with a default constructor that takes a name
16+
class LispDialect(name: String) extends Language {
17+
def isFunctional = true
18+
}
19+
20+
//objects are singletons
21+
object Scala extends Language {
22+
def isFunctional = true
23+
}
24+
25+
//you can override a method with a val
26+
object Java extends Language {
27+
val isFunctional = false
28+
}
29+
30+
/**
31+
* How do you think an Object is accessed?
32+
*/
33+
test("difference between class and object")({
34+
val classInstance = new LispDialect("Racket")
35+
assert(classInstance != null)
36+
37+
val objectInstance = ???
38+
assert(objectInstance != null)
39+
})
40+
41+
/**
42+
* Class can contain various members. They can be variables, values and methods (procedures or functions).
43+
* Return types are normally inferred by compiler, but it is a good style to used them.
44+
* Types of the parameters have to be supplied to compiler in method signatures.
45+
*/
46+
class Coder(val lang: Language = Java, var experience: Int = 0) {
47+
48+
// return type can be specified, don't need braces if it's just one line
49+
def function(s: String, i: Int): Int = s.toInt + i
50+
51+
// return type can also be inferred
52+
def position =
53+
if (experience > 4) "Senior"
54+
else "Junior"
55+
56+
//WARNING: Entering land of side effects !!!
57+
58+
// no '=' sign after definition, will return type 'Unit' which is like 'void'
59+
def procedure() {
60+
println("I'm just a side effect, since I return no value")
61+
}
62+
63+
// we can return 'Unit' even using the equals sign
64+
def stillAProcedure(param: String): Unit = {
65+
println("I'm still a side effect, I return Unit, even if you pass me a parameter" + param)
66+
}
67+
68+
//some pattern matching, we'll see it in a minute
69+
70+
}
71+
72+
/**
73+
* Congratulations, you just earned a year of experience
74+
*/
75+
test("re-assign variable") {
76+
val coder = new Coder
77+
78+
???
79+
80+
assert(coder.experience == 1)
81+
}
82+
83+
/**
84+
* We need a Ruby coder with 4 years of experience, and we need him now!!!
85+
*/
86+
test("construct an instance") {
87+
val coder = new Coder(???, ???)
88+
89+
assert(coder.lang === ???)
90+
assert(coder.experience === 4)
91+
92+
}
93+
94+
/**
95+
* Can you say what the function returns?
96+
* And what is the return type?
97+
*/
98+
test("return type of function") {
99+
val coder = new Coder(Scala)
100+
101+
val result = coder.position
102+
assert(result === ???)
103+
assert(result.getClass === classOf[????])
104+
}
105+
106+
/**
107+
* Class default constructor is in the the class signature and body
108+
* Parameters of the default constructor can have various visibilities. In our example:
109+
* - firstName is private value
110+
* - lastName is public value
111+
* - age is public variable
112+
* For some of them compiler generates accessors/mutators, but these does not follow Java rules.
113+
* They are named by fields without get/set prefixes. Using @BeanProperty annotation you can say
114+
* the compiler to generate also the getter/setter pair of the methods. In case of values only
115+
* accessor is generated.
116+
*/
117+
class FamousScalaCoder(firstName: String, val lastName: String, var age: Int) extends Coder(Scala, 10) {
118+
119+
val fullName =
120+
if (firstName == lastName) firstName
121+
else firstName + lastName // expression, part of default constructor
122+
123+
println("And " + fullName + " was born...") // part of default constructor, too
124+
125+
def this(nickname: String, age: Int) = { // auxiliary constructor
126+
this(nickname, nickname, age) // it has to call the default one as a 1st statement
127+
}
128+
129+
def this(nickname: String) = { // another auxiliary constructor
130+
this(nickname, -1) // it is allowed to call another auxiliary constructor as a 1st statement
131+
}
132+
}
133+
134+
/**
135+
* Can you change the above mentioned class and fill the ??? in asserts to make test pass?
136+
*/
137+
test("using default constructor and accessors/muttators")({
138+
val odersky = new FamousScalaCoder("Odersky", 55)
139+
odersky.age += 1
140+
141+
assert(odersky.age === ???)
142+
assert(odersky.experience === ???)
143+
})
144+
145+
/**
146+
* We need a class ScalaCoder where you can pass the years of experience as a parameter, can you take care of that?
147+
*/
148+
test("extending a class passing arguments to the superclass' default constructor ") {
149+
150+
//create a class where you can pass
151+
val you: Coder = ???
152+
assert(you.lang === Scala)
153+
}
154+
155+
/*
156+
* PATTERN MATCHING
157+
*/
158+
159+
/**
160+
* Pattern matching
161+
*
162+
* Pattern matching is like the 'switch-case' construct but much more powerful. It's a common feature of functional languages
163+
* and Scala has a very neat version of it
164+
*
165+
*/
166+
167+
def enjoysWork(coder: Coder) = coder.lang match {
168+
case Scala => "Sure!"
169+
case Java => "Less and less"
170+
case l: LispDialect => "Work? what work?"
171+
case _ => "not sure"
172+
}
173+
174+
test("identify the output of a match") {
175+
case class OS(name: String, version: Int)
176+
177+
def rate(os: OS) = os match {
178+
case OS("Android", 4) => 7
179+
case OS("Android", v) if v <= 4 => 6
180+
case OS("Firefox OS" | "Mint", _) => 5
181+
case OS("OS X", 10 | 9) => 4
182+
case OS("Windows", 8) | OS("Windows ME", _) => 3
183+
case _ => 0
184+
}
185+
186+
assert(7 == rate(???))
187+
assert(rate(OS("Windows", 8)) === ???)
188+
assert(rate(OS("OS X", 9)) === ???)
189+
assert(rate(OS("OS X", 11)) === ???)
190+
assert(rate(OS("Mint", 13)) === ???)
191+
assert(rate(OS("Mint", 14)) === ???)
192+
assert(6 == rate(???))
193+
194+
}
195+
196+
test("write a matcher to get this results") {
197+
198+
def matcher(l: List[Int]) = l match {
199+
200+
case List(a, b, c) => 1
201+
202+
case _ => ???
203+
}
204+
205+
assert(matcher(List()) == 0)
206+
assert(matcher(List(0, 0, 0)) == 1)
207+
assert(matcher(List(1, 1, 1)) == 1)
208+
assert(matcher(List(1, 2, 3, 4)) == 2)
209+
assert(matcher(List(1, 2, 4, 4, 5, 6)) == 3)
210+
assert(matcher(List(3, 3, 3)) == 1)
211+
assert(matcher(List(3, 3, 3, 4)) == 2)
212+
}
213+
214+
}

0 commit comments

Comments
 (0)