This repository was archived by the owner on Mar 5, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstruct-interface.go
More file actions
236 lines (196 loc) · 7.1 KB
/
Copy pathstruct-interface.go
File metadata and controls
236 lines (196 loc) · 7.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// Copyright 2018 Gytis Repečka. All rights reserved.
// Use of this source code is governed by a GNU GPL
// license that can be found in the LICENSE file.
package main
import (
"fmt"
)
// Define named structures
type CarMake struct {
makeTitle string
countryOfOrigin string
}
type Car struct {
make CarMake
model string
year int
started bool
}
type Truck struct {
modelName string
year int
started bool
weight int
}
// Interfaces
/*
Interfaces are implemented implicitly
A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.
Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.
Good example of interfaces: https://gobyexample.com/interfaces
*/
// Vehicle will act as interface to Car and Truck structs.
type Vehicle interface {
FriendlyName() string
Wheels() int
Start()
}
// Functions (methods)
// Receiving a pointer to Car/Truck so can modify it (change started value)
// Start method for Car
func (c *Car) Start() {
c.started = true
fmt.Printf("Car %s %s says vrooom!\n", c.make.makeTitle, c.model)
}
// Start method for Truck
func (t *Truck) Start() {
t.started = true
fmt.Printf("Truck %s (max cargo: %d t) says br br br br!\n", t.modelName, t.weight)
}
// Because defined only for Car and not listed as method in
// type Vehicle interface, StopCar is applicable only for Car
// but not Vehicle.
func (c *Car) StopCar() {
if c.started == true {
c.started = false
fmt.Printf("Car %s %s was stopped.\n", c.make.makeTitle, c.model)
} else {
fmt.Printf("Car %s %s is not running anyway.\n", c.make.makeTitle, c.model)
}
}
// This method is currently implemented for Car only. There is no implementation
// for Truck therefore cannot use this method for Vehicle.
func (cr *Car) AlterName(nameAppend string) (string) {
if nameAppend != "" {
cr.model = cr.model + " " + nameAppend
}
outString := fmt.Sprintf("%s %s (%d)", cr.make.makeTitle, cr.model, cr.year)
return outString
}
// Car has 4 wheels
func (cr Car) Wheels() int {
return 4
}
// Truck has 6 wheels
func (tr Truck) Wheels() int {
return 6
}
// ...and we can then use Wheels method to know how many wheels Vehicle has.
func (cr Car) FriendlyName() (string) {
outStarted := "not running"
if cr.started == true {
outStarted = "vuu tuuuu tuuuuu"
}
outString := fmt.Sprintf("%s %s (%d) %s", cr.make.makeTitle, cr.model, cr.year, outStarted)
return outString
}
func (t Truck) FriendlyName() (string) {
outStarted := "not running"
if t.started == true {
outStarted = "vuu tuuuu tuuuuu"
}
outString := fmt.Sprintf("%s (%d) %s", t.modelName, t.year, outStarted)
return outString
}
// This method is for Vehicle therefore universal (not specifically for Car or
// for Truck). Mind that inside we can use only those methods which are defined
// for Vehicle (i.e. for Car and for Truck).
func VehicleDetails(vh Vehicle) {
fmt.Printf("Vehicle %s has %d wheels.\n", vh.FriendlyName(), vh.Wheels())
}
// This method is intended for interface Vehicle and works for both Car and Truck
func VehicleStart(vh Vehicle) {
fmt.Println("Starting vehicle [from interface method]...")
vh.Start()
}
// Main function (execution point)
func main() {
var carMakes []CarMake
newCarMake := CarMake{makeTitle: "Toyota", countryOfOrigin: "Japan"}
carMakes = append(carMakes, newCarMake)
newCarMake = CarMake{makeTitle: "Renault", countryOfOrigin: "France"}
carMakes = append(carMakes, newCarMake)
newCarMake = CarMake{"Peugeot", "France"}
carMakes = append(carMakes, newCarMake)
var cars []Car
newCar := Car{carMakes[1], "Clio", 1994, false}
cars = append(cars, newCar)
newCar = Car{carMakes[2], "406", 1997, false}
cars = append(cars, newCar)
newCar = Car{carMakes[0], "Yaris", 2000, false}
cars = append(cars, newCar)
fmt.Println("--------------------------")
fmt.Println("Make | Model | Country")
fmt.Println("--------------------------")
for i:=0; i<len(cars); i++ {
fmt.Printf("%s | %s | %s\n", cars[i].make.makeTitle, cars[i].model, cars[i].make.countryOfOrigin)
}
fmt.Println("--------------------------")
// Let's execute struct function (method)
cars[2].Start()
cars[0].Start()
fmt.Println("--------------------------")
fmt.Println("Started cars:")
startedCars := 0
for i:=0; i<len(cars); i++ {
if cars[i].started == true {
fmt.Printf("%s %s\n", cars[i].make.makeTitle, cars[i].model)
startedCars++
}
}
if startedCars == 0 {
fmt.Println("No cars are started.")
} else {
fmt.Printf("Total: %d\n", startedCars)
}
fmt.Println("--------------------------")
fmt.Println("Will try out interfaces below:")
// Create new vehicle as existing car (Toyota Yaris (cars[2])
/*
Using address pointer (&) to cars[2] meaning that if we modify newVehicle
values, cars[2] will be affected because newVehicle is not a copy of cars[2]
but the same instance.
If we write newVehicle := cars[2] then "Read from cars array" would output
unchanged value (not "Yaris Limited Edition" but still "Yaris" even though
in newVehicle we whould have "Yaris Limited Edition").
More info about pointers: http://piotrzurek.net/2013/09/20/pointers-in-go.html
*/
// newVehicle := Car{carMakes[0], "RAV4", 2018, false}
// newVehicle := cars[2]
newVehicle := &cars[2]
fmt.Println("Starting newVehicle...")
// Since newVehicle is Car, we can apply Car's methods (Start and AlterName)
newVehicle.Start()
newVehicle.AlterName("Limited Edition")
// This method is defined for Vehicle therefore can be used on Car or Truck.
// VehicleDetails receives a copy of object (does not require pointer) so as
// underlying methods (FriendlyName and Wheels) that are called from VehicleDetails.
VehicleDetails(newVehicle)
fmt.Println("--------------------------")
fmt.Println("Read from cars array:")
fmt.Printf("%s %s\n", cars[2].make.makeTitle, cars[2].model)
// Because pointer was used, Car.model was updated.
fmt.Println("--------------------------")
fmt.Println("Let's try out Vehicle interface and it's method:")
fmt.Println("--------------------------")
// Let's create two Vehicle instances:
// - a Car using a pointer to Toyota Yaris
vehicleCar := &cars[2]
// - a new Truck
vehicleTruck := Truck{"Mercedes Actros", 2018, false, 10}
VehicleStart(vehicleCar)
/*
Must use pointer here - &vehicleTruck because Start expects pointer.
VehicleCar is already a pointer, so can use VehicleStart(vehicleCar)
whereas vehicleTruck we create now. If using VehicleStart(vehicleTruck)
(without pointer) this error shown:
.\struct-interface.go:229:15: cannot use vehicleTruck (type Truck) as type Vehicle in argument to VehicleStart:
Truck does not implement Vehicle (Start method has pointer receiver)
*/
VehicleStart(&vehicleTruck)
fmt.Println("--------------------------")
fmt.Println("Let's now show Vehicle in nicely formatted way:")
// As explained above, VehicleDetails and underlying methods receives a copy
// of object but we can pass a pointer to it as well.
VehicleDetails(&vehicleTruck)
}