I would advise reading Pragmatic Programmer 2nd edition contains a chapter about DRY (don’t repeat yourself) principle and why it’s not what people think it is. It’s important to note that reflection of what not to do is in 2nd edition only because people misunderstood it and made way more problems than improvements.
Also some lectures about DDD (domain driven design) and functional programming would be nice in their abstract sense of what they want to achieve and less of a how.
Now why would I suggest such “big stuff” to a student? Because your problems are found in many code-bases where seniors work. No amount of mathematics and philosophy about these and those machines will help you there - just a simple approach everybody hates: “use your head”. In fact knowing too much can also hurt because your head get’s fuzzy and you can’t decide.
Anyway, function name should be as clear as possible for a given context - this is why you separate them into modules or encapsulate in classes given you style of programming. For simplicity you can make everything global and just spread across properly named files.
Let’s say you have some code for accounting. It’s easy to make mistake and use DRY in the wrong way given example (I admit not the best example but you can find one in mentioned book:
function changeAmount(int amount) {
if (amount < 0) {
if (currentAmount < -1 * amount) raise Exception("Insuficient funds");
currentAmount += amount; // + because amount is negative
} else {
currentAmout += amount;
}
return currentAmount;
}
So we use same method to change currentAmount and we follow DRY right? Now what’s wrong here? We’ll let’s say you have bunch of code and now given some positive value want to withdraw from the account
function sendMoney(accountFrom, accountTo, int amount) {
amountFrom = accountFrom.changeAmount(-1 * amount);
amountTo = accountTo.changeAmount(amount);
.....
sendMessage("left on account " + amountFrom);
sendMessage("account balance to " + amountTo");
}
Now this is obviously bad since we need to think all the time about which amount should be positive, which negative. This can lead to confusion and even errors which cannot be detected as both - and + values are accepted and decide what to do with the value. Code in the function is ugly and less readable. Also description of the method “changeAmount” doesn’t tell us anything about domain of application - we don’t know how would it be explained in real world.
We can do a bit better with 2 functions
function withdraw(int amount) {
if (amount <= 0) raise Exception("Illegal value");
if (currentAmount < amount) raise Exception("Insuficient funds");
currentAmount -= amount;
return currentAmount;
}
function deposit(int amount) {
if (amount <= 0) raise Exception("Illegal value");
currentAmount += amount;
return currentAmount;
}
function sendMoney(accountFrom, accountTo, int amount) {
amountFrom = accountFrom.withdraw(amount);
amountTo = accountTo.deposit(amount);
.....
sendMessage("left on account " + amountFrom);
sendMessage("account balance to " + amountTo");
}
Note: code is not from any language just my attempt to make example pythonic as possible :D.
Although example isn’t the best one you can see from the new code why would someone get tricked into using 1 function. sendMoney didn’t change much except that it calls 2 different methods now. However maintaining large codebase is the reason why to keep it as descriptive as possible. There’s less chance of error since all the requested amounts must be positive. Code clearly states what should happen in the real world. We do have 2 lines of same code - one for throwing exception on negative values and return of the current value. Second way is preferred way of splitting things even though you’re effectively changing the same value. However consider others reading your code and trying to figure out where the bug might be or such. Sending in amount of -50 from external parties would result in accountFrom being increased due to -1 * amount trigger and decreasing accountTo which is reverse of what we want. If you send -50 in second scenario it fails always as transactions must always have positive value of transfer.
This is all under assumption that you’re working on “average” type of software. Applications for embedded systems or such are forced sometimes to use different styles.
Sorry for the long answer here’s a potato