-
-
Save tuannguyenssu/47ce898339f16a2673ac221f55b08160 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//-------------------------------------------------------------------------------------- | |
// 3. Function - Hàm | |
//-------------------------------------------------------------------------------------- | |
//-------------------------------------------------------------------------------------- | |
// Tránh side effects | |
// Bad | |
// Global variable referenced by following function. | |
// If we had another function that used this name, now it'd be an array and it could break it. | |
var name = 'Ryan McDermott'; | |
public string SplitIntoFirstAndLastName() | |
{ | |
return name.Split(" "); | |
} | |
SplitIntoFirstAndLastName(); | |
Console.PrintLine(name); // ['Ryan', 'McDermott']; | |
// Good | |
public string SplitIntoFirstAndLastName(string name) | |
{ | |
return name.Split(" "); | |
} | |
var name = 'Ryan McDermott'; | |
var newName = SplitIntoFirstAndLastName(name); | |
Console.PrintLine(name); // 'Ryan McDermott'; | |
Console.PrintLine(newName); // ['Ryan', 'McDermott']; | |
//-------------------------------------------------------------------------------------- | |
// Tránh điều kiện negative false | |
// Bad | |
public bool IsDOMNodeNotPresent(string node) | |
{ | |
// ... | |
} | |
if (!IsDOMNodeNotPresent(node)) | |
{ | |
// ... | |
} | |
// Good | |
public bool IsDOMNodePresent(string node) | |
{ | |
// ... | |
} | |
if (IsDOMNodePresent(node)) | |
{ | |
// ... | |
} | |
//-------------------------------------------------------------------------------------- | |
// Hạn chế dùng điều kiện mà thay vào đó hãy dùng kế thừa | |
// Bad | |
class Airplane | |
{ | |
// ... | |
public double GetCruisingAltitude() | |
{ | |
switch (_type) | |
{ | |
case '777': | |
return GetMaxAltitude() - GetPassengerCount(); | |
case 'Air Force One': | |
return GetMaxAltitude(); | |
case 'Cessna': | |
return GetMaxAltitude() - GetFuelExpenditure(); | |
} | |
} | |
} | |
// Good | |
interface IAirplane | |
{ | |
// ... | |
double GetCruisingAltitude(); | |
} | |
class Boeing777 : IAirplane | |
{ | |
// ... | |
public double GetCruisingAltitude() | |
{ | |
return GetMaxAltitude() - GetPassengerCount(); | |
} | |
} | |
class AirForceOne : IAirplane | |
{ | |
// ... | |
public double GetCruisingAltitude() | |
{ | |
return GetMaxAltitude(); | |
} | |
} | |
class Cessna : IAirplane | |
{ | |
// ... | |
public double GetCruisingAltitude() | |
{ | |
return GetMaxAltitude() - GetFuelExpenditure(); | |
} | |
} | |
//-------------------------------------------------------------------------------------- | |
// Tránh kiểm tra kiểu dữ liệu | |
// Bad | |
public Path TravelToTexas(object vehicle) | |
{ | |
if (vehicle.GetType() == typeof(Bicycle)) | |
{ | |
(vehicle as Bicycle).PeddleTo(new Location("texas")); | |
} | |
else if (vehicle.GetType() == typeof(Car)) | |
{ | |
(vehicle as Car).DriveTo(new Location("texas")); | |
} | |
} | |
public int Combine(dynamic val1, dynamic val2) | |
{ | |
int value; | |
if (!int.TryParse(val1, out value) || !int.TryParse(val2, out value)) | |
{ | |
throw new Exception('Must be of type Number'); | |
} | |
return val1 + val2; | |
} | |
// Good | |
public Path TravelToTexas(Traveler vehicle) | |
{ | |
vehicle.TravelTo(new Location("texas")); | |
} | |
// pattern matching | |
public Path TravelToTexas(object vehicle) | |
{ | |
if (vehicle is Bicycle bicycle) | |
{ | |
bicycle.PeddleTo(new Location("texas")); | |
} | |
else if (vehicle is Car car) | |
{ | |
car.DriveTo(new Location("texas")); | |
} | |
} | |
public int Combine(int val1, int val2) | |
{ | |
return val1 + val2; | |
} | |
//-------------------------------------------------------------------------------------- | |
// Tránh đặt các cờ kiểu bool trong parameters | |
// Bad | |
public void CreateFile(string name, bool temp = false) | |
{ | |
if (temp) | |
{ | |
Touch("./temp/" + name); | |
} | |
else | |
{ | |
Touch(name); | |
} | |
} | |
// Good | |
public void CreateFile(string name) | |
{ | |
Touch(name); | |
} | |
public void CreateTempFile(string name) | |
{ | |
Touch("./temp/" + name); | |
} | |
//-------------------------------------------------------------------------------------- | |
// Không được write vào các hàm toàn cục | |
// Bad | |
public string[] Config() | |
{ | |
return [ | |
"foo" => "bar", | |
] | |
} | |
// Good | |
class Configuration | |
{ | |
private string[] _configuration = []; | |
public Configuration(string[] configuration) | |
{ | |
_configuration = configuration; | |
} | |
public string[] Get(string key) | |
{ | |
return (_configuration[key]!= null) ? _configuration[key] : null; | |
} | |
} | |
// Load configuration and create instance of Configuration class | |
var configuration = new Configuration(new string[] { | |
"foo" => "bar", | |
}); | |
//-------------------------------------------------------------------------------------- | |
// Hạn chế sử dụng Singleton thay vào đó hãy dùng options | |
// Bad | |
class DBConnection | |
{ | |
private static DBConnection _instance; | |
private DBConnection() | |
{ | |
// ... | |
} | |
public static GetInstance() | |
{ | |
if (_instance == null) | |
{ | |
_instance = new DBConnection(); | |
} | |
return _instance; | |
} | |
// ... | |
} | |
var singleton = DBConnection.GetInstance(); | |
// Good | |
class DBConnection | |
{ | |
public DBConnection(IOptions<DbConnectionOption> options) | |
{ | |
// ... | |
} | |
// ... | |
} | |
// Create instance of DBConnection class and configure it with Option pattern. | |
var options = <resolve from IOC>; | |
var connection = new DBConnection(options); | |
//-------------------------------------------------------------------------------------- | |
// Nên sử dụng tối đa 2 parameters cho một hàm | |
// Bad | |
public void CreateMenu(string title, string body, string buttonText, bool cancellable) | |
{ | |
// ... | |
} | |
// Good | |
pubic class MenuConfig | |
{ | |
public string Title { get; set; } | |
public string Body { get; set; } | |
public string ButtonText { get; set; } | |
public bool Cancellable { get; set; } | |
} | |
var config = new MenuConfig | |
{ | |
Title = "Foo", | |
Body = "Bar", | |
ButtonText = "Baz", | |
Cancellable = true | |
}; | |
public void CreateMenu(MenuConfig config) | |
{ | |
// ... | |
} | |
//-------------------------------------------------------------------------------------- | |
// Một hàm nên và chỉ nên làm một nhiệm vụ duy nhất | |
// Bad | |
public void SendEmailToListOfClients(string[] clients) | |
{ | |
foreach (var client in clients) | |
{ | |
var clientRecord = db.Find(client); | |
if (clientRecord.IsActive()) | |
{ | |
Email(client); | |
} | |
} | |
} | |
// Good | |
public void SendEmailToListOfClients(string[] clients) | |
{ | |
var activeClients = GetActiveClients(clients); | |
// Do some logic | |
} | |
public List<Client> GetActiveClients(string[] clients) | |
{ | |
return db.Find(clients).Where(s => s.Status == "Active"); | |
} | |
//-------------------------------------------------------------------------------------- | |
// Đặt tên hàm theo mục đích rõ ràng | |
// Bad | |
public class Email | |
{ | |
//... | |
public void Handle() | |
{ | |
SendMail(this._to, this._subject, this._body); | |
} | |
} | |
var message = new Email(...); | |
// What is this? A handle for the message? Are we writing to a file now? | |
message.Handle(); | |
// Good | |
public class Email | |
{ | |
//... | |
public void Send() | |
{ | |
SendMail(this._to, this._subject, this._body); | |
} | |
} | |
var message = new Email(...); | |
// Clear and obvious | |
message.Send(); | |
//-------------------------------------------------------------------------------------- | |
// Functions should only be one level of abstraction | |
// Bad | |
public string ParseBetterJSAlternative(string code) | |
{ | |
var regexes = [ | |
// ... | |
]; | |
var statements = explode(" ", code); | |
var tokens = new string[] {}; | |
foreach (var regex in regexes) | |
{ | |
foreach (var statement in statements) | |
{ | |
// ... | |
} | |
} | |
var ast = new string[] {}; | |
foreach (var token in tokens) | |
{ | |
// lex... | |
} | |
foreach (var node in ast) | |
{ | |
// parse... | |
} | |
} | |
// Good | |
class Tokenizer | |
{ | |
public string Tokenize(string code) | |
{ | |
var regexes = new string[] { | |
// ... | |
}; | |
var statements = explode(" ", code); | |
var tokens = new string[] {}; | |
foreach (var regex in regexes) | |
{ | |
foreach (var statement in statements) | |
{ | |
tokens[] = /* ... */; | |
} | |
} | |
return tokens; | |
} | |
} | |
class Lexer | |
{ | |
public string Lexify(string[] tokens) | |
{ | |
var ast = new[] {}; | |
foreach (var token in tokens) | |
{ | |
ast[] = /* ... */; | |
} | |
return ast; | |
} | |
} | |
class BetterJSAlternative | |
{ | |
private string _tokenizer; | |
private string _lexer; | |
public BetterJSAlternative(Tokenizer tokenizer, Lexer lexer) | |
{ | |
_tokenizer = tokenizer; | |
_lexer = lexer; | |
} | |
public string Parse(string code) | |
{ | |
var tokens = _tokenizer.Tokenize(code); | |
var ast = _lexer.Lexify(tokens); | |
foreach (var node in ast) | |
{ | |
// parse... | |
} | |
} | |
} | |
//-------------------------------------------------------------------------------------- | |
// Các hàm callers và callees nên đặt gần nhau | |
// Bad | |
class PerformanceReview | |
{ | |
private readonly Employee _employee; | |
public PerformanceReview(Employee employee) | |
{ | |
_employee = employee; | |
} | |
private IEnumerable<PeersData> LookupPeers() | |
{ | |
return db.lookup(_employee, 'peers'); | |
} | |
private ManagerData LookupManager() | |
{ | |
return db.lookup(_employee, 'manager'); | |
} | |
private IEnumerable<PeerReviews> GetPeerReviews() | |
{ | |
var peers = LookupPeers(); | |
// ... | |
} | |
public PerfReviewData PerfReview() | |
{ | |
GetPeerReviews(); | |
GetManagerReview(); | |
GetSelfReview(); | |
} | |
public ManagerData GetManagerReview() | |
{ | |
var manager = LookupManager(); | |
} | |
public EmployeeData GetSelfReview() | |
{ | |
// ... | |
} | |
} | |
var review = new PerformanceReview(employee); | |
review.PerfReview(); | |
// Good | |
class PerformanceReview | |
{ | |
private readonly Employee _employee; | |
public PerformanceReview(Employee employee) | |
{ | |
_employee = employee; | |
} | |
public PerfReviewData PerfReview() | |
{ | |
GetPeerReviews(); | |
GetManagerReview(); | |
GetSelfReview(); | |
} | |
private IEnumerable<PeerReviews> GetPeerReviews() | |
{ | |
var peers = LookupPeers(); | |
// ... | |
} | |
private IEnumerable<PeersData> LookupPeers() | |
{ | |
return db.lookup(_employee, 'peers'); | |
} | |
private ManagerData GetManagerReview() | |
{ | |
var manager = LookupManager(); | |
return manager; | |
} | |
private ManagerData LookupManager() | |
{ | |
return db.lookup(_employee, 'manager'); | |
} | |
private EmployeeData GetSelfReview() | |
{ | |
// ... | |
} | |
} | |
var review = new PerformanceReview(employee); | |
review.PerfReview(); | |
//-------------------------------------------------------------------------------------- | |
// Nên gom các biểu thức điều kiện và tách thành hàm riêng | |
// Bad | |
if (article.state == "published") | |
{ | |
// ... | |
} | |
// Good | |
if (article.IsPublished()) | |
{ | |
// ... | |
} | |
//-------------------------------------------------------------------------------------- | |
// Xóa hết các code không dùng | |
// Bad | |
public void OldRequestModule(string url) | |
{ | |
// ... | |
} | |
public void NewRequestModule(string url) | |
{ | |
// ... | |
} | |
var request = NewRequestModule(requestUrl); | |
InventoryTracker("apples", request, "www.inventory-awesome.io"); | |
// Good | |
public void RequestModule(string url) | |
{ | |
// ... | |
} | |
var request = RequestModule(requestUrl); | |
InventoryTracker("apples", request, "www.inventory-awesome.io"); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment