While learning Swift on your own, you will use the output to flesh out the knowledge you have learned. It is the first time to output itself, and it also serves as an output practice.
Learn while refactoring a class of rectangular parallelepipeds whose sides are Int type.
To be able to use the learned knowledge by coding it in a form close to practice.
-About the usefulness of ** willSet ** and ** didSet ** --Usefulness of ** Read Only Property ** using Compute Properties --Resolving the violation of the DRY principle
--Has Int type height and width properties --The maximum on one side is 6 --Has an Int type volume property --Has an enum-type size (S, M, L) property that changes with volume --The size condition is S (1 ... 8) M (9 ... 64) L (65 ... 216)
class IntBox{
enum Size{
case S, M, L
}
//Horizontal and vertical height
var width: Int
var height: Int
var depth: Int
//volume
var volume: Int
//size
var size:Size
init(w: Int, h: Int, d: Int){
//Initialization of horizontal and vertical height
width = w
height = h
depth = d
//Volume initialization
volume = w * h * d
//Size initialization
switch volume{
case 1...8:
size = .S
case 9...64:
size = .M
default:
size = .L
}
}
//A function that displays a status list is provided for verification.
func printStatus(){
var status = """
---status---
width: \(width)
height: \(height)
depth: \(depth)
volume: \(volume)
size: \(size)
"""
print(status)
}
}
let cb = IntBox(w: 1, h: 1, d: 1)
cb.printStatus()
//Try changing the width
cb.width = 11
cb.printStatus()
//Try changing the volume
cb.volume = -20
cb.printStatus()
//Try changing the size
cb.size = .L
cb.printStatus()
Execution result
---status---
width: 1
height: 1
depth: 1
volume: 1
size: S
---status---
width: 11
height: 1
depth: 1
volume: 1 #Should change!!
size: S
---status---
width: 11
height: 1
depth: 1
volume: -20 #It is strange that 0 or less can not be set(Volume depends on the length of each side)
size: S
---status---
width: 11
height: 1
depth: 1
volume: -20
size: L #It is strange that it can be set as well as the volume. Think about dependencies
So I will solve it one by one.
[Pushing point](#Pushing point) 1 and 2 are solved.
1 The condition of a maximum of 6 on each side is ignored.
2 No error occurs when the length of one side is 0 or less.
Insert the following at the beginning of the initialization.
if w <= 0 || w > 6 {fatalError("w:\(w) not in 1...6")}
if h <= 0 || h > 6 {fatalError("h:\(h) not in 1...6")}
if d <= 0 || d > 6 {fatalError("d:\(d) not in 1...6")}
[Pushing point](#Pushing point) 3 is solved.
The volume and size do not change even if the length of the three sides is changed.
I want to change the volume and size when the height and height values change. Add ** willSet ** and ** didSet ** to each property and set the volume and size there.
Validation means verifying whether you can set the length you want to change, so before setting it. After setting, the volume and size will change due to the change in length.
--willSet: Validation --detSet: Volume and size set
var width: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<width: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
var height: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<height: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
var depth: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<depth: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
[Pushing point](#Pushing point) Solves 4 and 5.
4 The volume can be changed from the outside. 5 The size can be changed from the outside.
Setting it to Read Only Property makes it impossible to set.
--Added get {} to volume and size --Keep data in private var _volume and private var _size that cannot be seen from the outside.
private var _volume: Int
private var _size: Size
var volume: Int {
get {
return _volume
}
}
var size: Size {
get {
return _size
}
}
.. .. .. It's verbose code as you can imagine.
class IntBox{
enum Size{
case S, M, L
}
private var _volume: Int
private var _size: Size
var width: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<width: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
var height: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<height: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
var depth: Int{
willSet{
if newValue<= 0 || newValue> 6 {fatalError("<depth: \(newValue)> not in 1...6")}
}
didSet{
_volume = width * height * depth
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
}
var volume: Int {
get {
return _volume
}
}
var size: Size {
get {
return _size
}
}
init(w: Int, h: Int, d: Int){
if w <= 0 || w > 6 {fatalError("<w: \(w)> not in 1...6")}
if h <= 0 || h > 6 {fatalError("<h: \(h)> not in 1...6")}
if d <= 0 || d > 6 {fatalError("<d: \(d)> not in 1...6")}
width = w
height = h
depth = d
_volume = w * h * d
switch _volume{
case 1...8:
_size = .S
case 9..<64:
_size = .M
default:
_size = .L
}
}
func printStatus(){
var status = """
---status---
width: \(width)
height: \(height)
depth: \(depth)
volume: \(volume)
size: \(size)
"""
print(status)
}
}
let cb = IntBox(w: 1, h: 1, d: 1)
cb.printStatus()
//Try changing the width
cb.width = 11
cb.printStatus()
Since there are many violations of the DRY principle, I will create a common function and put it together.
private func lengthValidate(_ length: Int, _ name: String = "length"){
if length <= 0 || length > 6 { fatalError("<\(name): \(length)> not in 1...6") }
}
private func setStatus() {
setVolume()
setSize()
}
private func setVolume() {
_volume = width * height * depth
}
private func setSize(){
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
It would be nice if ** setStatus () ** could be created when volume or size is referenced. However, there is no change I also want to avoid calling ** setStatus () ** every time volume or size is referenced. I want to call ** setStatus () ** only once after changing the height and width. ** private var shouldSetStatus: Creates a Bool ** and evaluates whether to call ** setStatus () **.
private var shouldSetStatus: Bool
var width: Int = 1 {
willSet { lengthValidate(newValue, "width") }
didSet { shouldSetStatus = true } // <-Change
}
...
var volume: Int {
get {
if shouldSetStatus { setStatus() }
return _volume
}
}
...
private func setStatus() {
setVolume()
setSize()
shouldSetStatus = false // <-add to
}
class IntBox{
enum Size{
case S, M, L
}
private var _volume: Int = 1
private var _size: Size = .S
private var shouldSetStatus: Bool = false
var width: Int = 1 {
willSet { lengthValidate(newValue, "width") }
didSet { shouldSetStatus = true }
}
var height: Int = 1 {
willSet { lengthValidate(newValue, "height") }
didSet { shouldSetStatus = true }
}
var depth: Int = 1 {
willSet { lengthValidate(newValue, "depth") }
didSet { shouldSetStatus = true }
}
var volume: Int {
get {
if shouldSetStatus { setStatus() }
return _volume
}
}
var size: Size {
get {
if shouldSetStatus { setStatus() }
return _size
}
}
init(w: Int, h: Int, d: Int){
lengthValidate(w, "w")
lengthValidate(h, "h")
lengthValidate(d, "d")
width = w
height = h
depth = d
setStatus()
}
private func lengthValidate(_ length: Int, _ name: String = "length"){
if length <= 0 || length > 6 { fatalError("<\(name): \(length)> not in 1...6") }
}
private func setStatus() {
setVolume()
setSize()
shouldSetStatus = false
}
private func setVolume() {
_volume = width * height * depth
}
private func setSize(){
switch _volume{
case 1...8:
_size = .S
case 9...64:
_size = .M
default:
_size = .L
}
}
//Verification code
func printStatus(){
let status = """
---status---
width: \(width)
height: \(height)
depth: \(depth)
volume: \(volume)
size: \(size)
"""
print(status)
}
}
//Verification
let cb = IntBox(w: 1, h: 2, d: 3)
cb.printStatus()
//Try changing the width
cb.width = 5
cb.printStatus()
Originally, it was designed from an abstract protocol so that the maximum value of one side and the evaluation of S, M, L could be changed flexibly. Regarding the error, it is only ** fatalError ** without using ** Optional **, but it is better to return ** nil ** at the time of initialization or separate it from the error at the time of length change. It may be good. .. .. I once wrote an article about my current best efforts. Someday I'll grow to the point where this seems like an embarrassing article. I also spent 2 hours x 3 days on this article. .. .. I will write an article considering cost performance.
Recommended Posts