Pragmatic Programmer Slide
Pragmatic Programmer
What is difference between "programming" vs "Software Engineering"?
Problem Solving
"Fix" error, not "turn off" error notification
Dive deep, analysis the problem
Nature of Software Engineering
Create "value"
Challenge
always changes
requirements, technologies, knowledges, emotions :)
Consequences
"Cost" increased
resource cost, human cost
Target
Create "value" with minimal "cost"
Solution
Reduce the changing cost by making system
Easier to Change
How?
Best practices, coding principles, pattern design, testing, etc
Don't Repeat Yourself (DRY)
avoid
duplicated a piece of knowledge
and
duplicated code ≠ duplicated knowledge
def validate_age(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
def validate_quantity(value):
validate_type(value, :integer)
validate_min_integer(value, 0)
DRY
duplicated between document and code
# Calculate the fees for this account.
# * Each returned check costs $20
# * If the account is in overdraft for more than 3 days,
# charge $10 for each day
# * If the average account balance is greater that $2,000
# reduce the fees by 50%
def fees(a)
f = 0
if a.returned_check_count > 0
f += 20 * a.returned_check_count
end
if a.overdraft_days > 3
f += 10*a.overdraft_days
end
if a.average_balance > 2_000
f /= 2
end
f
end
solution
change layout, better naming
def calculate_account_fees(account)
fees = 20 * account.returned_check_count
fees += 10 * account.overdraft_days if account.overdraft_days > 3
fees /= 2 if account.average_balance > 2_000
fees
end
DRY Violation in Data
class Line
{
Point start;
Point end;
double length;
};
assumpt: $length = start - end$
length information has two sources
sooner or later those source will be conflicted
Solution
using setter, getter
def getLength()
{
return abs(start - end)
}
### another option ###
def calculateLength()
{
length = abs( start - end)
}
def setStart(Point newStart)
{
start = newStart
calculateLength()
}
Use "value" to guide the choice of solution
Knowledges Synchronization
avoid
repeat work
Make it Easy to Reuse
Orthogonality
a single, well-defined responsibility
Benefit
change in one does not affect the others
How to implement?
modular, component-based, layers-based
well-defined responsibility
eliminate the redundant, small, easy to track
avoid
coupling
coupling is the degree of interdependence between modules (class, function, data, etc)
How to avoid coupling?
Testing
Testing is not about finding bugs
The first User of your code
It gives feedback about your implementation design
Test Early, Test Often, Test Automatically
Testing
class Database:
def __init__(self):
self.data = []
def add_data(self, item):
self.data.append(item)
def get_data(self):
return self.data
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def save(self):
database = Database()
database.add_data({"name": self.name, "age": self.age})
class TestUser(unittest.TestCase):
def test_save(self):
user = User("John", 30)
user.save()
database = Database()
data = database.get_data()
self.assertEqual(len(data), 1)
self.assertEqual(data[0]["name"], "John")
self.assertEqual(data[0]["age"], 30)
Not know where to start
Tracer Bullets
Get feedback as soon as possible
Coding
Impossible Assumption
- A month with fewer than 28 days
- Error code from a system call: can’t access the current directory
- In C++: a = 2; b = 3; but (a + b) does not equal 5
- A triangle with an interior angle sum ≠ 180°
- A minute that doesn’t have 60 seconds
- (a + 1) <= a
Coding
Assertive Programming
put assert
statement to check "impossible" state
Coding
Decoupling
Type of couplings:
- Train wrecks
- Globalization
Decoupling: Train wrecks
public void applyDiscount(customer, order_id, discount)
{
totals = customer.orders.find(order_id).getTotals();
totals.grandTotal = totals.grandTotal - discount;
totals.discount = discount;
}
5 abtraction levels: implicit knowledge
Client request: no order has discount more than 40%
where to change?
Decoupling: Train wrecks
Tell, Don’t Ask
should not make decisions based on the internal state of an object and then update that object
class Total
{
public void applyDiscount(discount)
{
this.discount = discount;
grandTotal -= this.discount;
}
}
public void applyDiscount(customer, order_id, discount)
{
customer.orders.find(order_id).getTotals()
.applyDiscount(discount);
}
Decoupling: Train wrecks
Tell, Don’t Ask
should not make decisions based on the internal state of an object and then update that object
class Total
{
public void applyDiscount(discount)
{
this.discount = discount;
grandTotal -= this.discount;
}
}
class Order
{
public void applyDiscount(discount)
{
getTotals().applyDiscount(discount);
}
}
public void applyDiscount(customer, order_id, discount)
{
customer.orders.find(order_id).applyDiscount(discount);
}
Decoupling: Globalization
global data is available inside
every
method