The operators in a programming language, in general, and in Swift, in particular, are of three types:
Unary - that operate on a single target. They can be added before or after the target.
For example -a or !a or a!.
They can also be binary when they are applied to two targets. For example: a + b.
There are also the ternary operators that are applied to three targets. In Swift, we have only one ternary operator that has the format:
condition ? value_if_true : value_if_false
For example:
var interestRate = income < 1000 ? 3 : 5
It is a useful shortcut for
If income < 1000 {
interestRate = 3
} else {
interestRate = 5
}
There are some cases when the ternary operator can become confusing.
For example, in the case when the options for true and false are pretty long:
let message = isAdmin ? “Please enter your admin username and password” : “Please enter your username and password”
Here, and if statement would me the code better
var message: String
If isAdmin {
message = “Please enter your admin username and password”
} else {
message = “Please enter your username and password”
}
So, as a conclusion, the ternary operator is good but sometimes replacing it with if statement might be better.
Of course, in our case, the strings need to be replaced with constants and maybe localized, but that is a different topic for another time.
Friday, February 10, 2017
Friday, February 3, 2017
Use long names
Unfortunately, we have been used to the fact that a name has nothing to do with the object it names. John, Mary, and Steve say almost nothing about the people it names. Maybe just that they all speak English. We don’t know if the are tall or short if they are good with computers or know how to paint or play the piano.
Of course, the reason for that is that their parents had no idea how would their kids behave when they would grow up.
But, fortunately, it is different when we write the code. When we create an element of our code, it does not matter if it is a constant, variable, method or a class, we know for sure what it will be used for. So we are in a better position to choose a name. And even if we are wrong and the purpose of the element will evolve, we can change the name anytime. Most of the modern IDE-s have a refactoring option that can change a name across the project.
Another great feature the IDE-s of today have is that they prepopulate the names. You start typing a couple of letters and they give you a dropdown to select the best options for the specific context. So we don’t really type the whole names anymore.
And because of this, there is no reason to save the space and use short names. We can use names as long as we want if they are useful.
Let’s see few examples:
age -> userAge
error -> connectionErrorCode
name -> dogName
User -> UserView
Login -> LoginViewController
message -> errorMessageAfterLogin
The advantage is obvious. So go crazy!
Of course, the reason for that is that their parents had no idea how would their kids behave when they would grow up.
But, fortunately, it is different when we write the code. When we create an element of our code, it does not matter if it is a constant, variable, method or a class, we know for sure what it will be used for. So we are in a better position to choose a name. And even if we are wrong and the purpose of the element will evolve, we can change the name anytime. Most of the modern IDE-s have a refactoring option that can change a name across the project.
Another great feature the IDE-s of today have is that they prepopulate the names. You start typing a couple of letters and they give you a dropdown to select the best options for the specific context. So we don’t really type the whole names anymore.
And because of this, there is no reason to save the space and use short names. We can use names as long as we want if they are useful.
Let’s see few examples:
age -> userAge
error -> connectionErrorCode
name -> dogName
User -> UserView
Login -> LoginViewController
message -> errorMessageAfterLogin
The advantage is obvious. So go crazy!
Wednesday, February 1, 2017
Write tests
Welcome to another post about how to improve our code.
Today we are trying to prove that the quality of the code improves when we write tests.
We, as developers, hate to write tests. Why? Simple. It brings extra work without no real immediate advantage. Also, because we are the ones writing the code, we expect it to be perfect. So, why test a perfect code?
Our managers hate the activity as well. Why? Because it costs them extra money and time without adding new features for the customers.
Then why do it? From the same reason, you test any equipment you buy from any store. You expect it to work, right? They probably test it before bringing it to the shop. And yet you thoroughly test it because you know, from experience, that some do fail.
When we write code we consider only some specific scenarios that we call sometimes happy paths. Then we implement the code according to only those.
It does not matter how you test your code. Ideally, you should use a framework (like XCTest), but you might as well write your own framework.
Let's take an example.
Assuming you work at a bank and you need a function to calculate the maximum interest rate a client can pay. As input, you have the mortgage he needs to pay per year and his available funds.
So assuming a client needs to pay $11 000 per year and his available funds, after deducting all the expenses from his salary, are $10 000, it is obvious that the maximum rate he can pay is 10%.
To calculate it we use the following formula
Rate = ((available funds - mortgage per year) / mortgage per year) * 100
so (11 000 - 10 000) / 10 000 * 100 = 10
let's quickly write the function to calculate that
let availableFunds = 11000
let mortgage = 10000
func computeMaximumInterestRate(availableFunds: Double, mortgage: Double) -> Double {
return (availableFunds - mortgage) / mortgage * 100
}
Everything looks great. Let’s use this method:
computeMaximumInterestRate(availableFunds: 11000, mortgage: 10000)
All is good, it returns 10 meaning 10% as expected.
Now let’s take the tester hat and go crazy.
computeMaximumInterestRate(availableFunds: 10000, mortgage: 11000)
This returns 0. It means the customer can pay 0%. It might be okay, but in this case, the function should show that the user cannot really take this mortgage.
How about :
computeMaximumInterestRate(availableFunds: 9000, mortgage: -10000)
This returns -10. Is this a valid result? Not really. This means that if the client takes the mortgage, the bank will actually pay him some money.
This is similar to the famous bug Amazon had when they launched. If a user entered a negative number of items, the website will actually send some money to his credit card.
And finally:
computeMaximumInterestRate(availableFunds: 0, mortgage: 0)
This will crash the app.
We will not go into details about how to fix each of this problems, but we know that we need to fix them. Our function should differentiate between a valid result (like 10%) and an invalid one (like -10%) and maybe not return it at all, just raise an exception.
That’s it for now. See you next time!
Today we are trying to prove that the quality of the code improves when we write tests.
We, as developers, hate to write tests. Why? Simple. It brings extra work without no real immediate advantage. Also, because we are the ones writing the code, we expect it to be perfect. So, why test a perfect code?
Our managers hate the activity as well. Why? Because it costs them extra money and time without adding new features for the customers.
Then why do it? From the same reason, you test any equipment you buy from any store. You expect it to work, right? They probably test it before bringing it to the shop. And yet you thoroughly test it because you know, from experience, that some do fail.
When we write code we consider only some specific scenarios that we call sometimes happy paths. Then we implement the code according to only those.
It does not matter how you test your code. Ideally, you should use a framework (like XCTest), but you might as well write your own framework.
Let's take an example.
Assuming you work at a bank and you need a function to calculate the maximum interest rate a client can pay. As input, you have the mortgage he needs to pay per year and his available funds.
So assuming a client needs to pay $11 000 per year and his available funds, after deducting all the expenses from his salary, are $10 000, it is obvious that the maximum rate he can pay is 10%.
To calculate it we use the following formula
Rate = ((available funds - mortgage per year) / mortgage per year) * 100
so (11 000 - 10 000) / 10 000 * 100 = 10
let's quickly write the function to calculate that
let availableFunds = 11000
let mortgage = 10000
func computeMaximumInterestRate(availableFunds: Double, mortgage: Double) -> Double {
return (availableFunds - mortgage) / mortgage * 100
}
Everything looks great. Let’s use this method:
computeMaximumInterestRate(availableFunds: 11000, mortgage: 10000)
All is good, it returns 10 meaning 10% as expected.
Now let’s take the tester hat and go crazy.
computeMaximumInterestRate(availableFunds: 10000, mortgage: 11000)
This returns 0. It means the customer can pay 0%. It might be okay, but in this case, the function should show that the user cannot really take this mortgage.
How about :
computeMaximumInterestRate(availableFunds: 9000, mortgage: -10000)
This returns -10. Is this a valid result? Not really. This means that if the client takes the mortgage, the bank will actually pay him some money.
This is similar to the famous bug Amazon had when they launched. If a user entered a negative number of items, the website will actually send some money to his credit card.
And finally:
computeMaximumInterestRate(availableFunds: 0, mortgage: 0)
This will crash the app.
We will not go into details about how to fix each of this problems, but we know that we need to fix them. Our function should differentiate between a valid result (like 10%) and an invalid one (like -10%) and maybe not return it at all, just raise an exception.
That’s it for now. See you next time!
Subscribe to:
Posts (Atom)