สรุป design software principle สำหรับเป็น guidelines เพื่อใช้ในการ design software ซึ่งโดยทั่วไปแล้ว pattern และ practices ก็เหมือนเครื่องเมื่อ (tools) เพื่อที่จะทำให้สามารถสร้าง software ที่มีคุณภาพ ตัวอย่างเช่น
- KISS – Keep it simple, stupid ใช้หลักการออกแบบที่ง่าย ไม่ซับซ้อน จะทำให้ software ทำงานได้ดีกว่า
- DRY – Don’t repeat yourself หลีกเลี่ยงการทำอะไรซ้ำๆ หรือมีส่ิงเดียวกันหลายๆ ที่ ทำให้แก้ไขลำบาก
- YAGNI – You aren’t gonna need it ไม่ควรพัฒนาหรือเพิ่ม function ใดๆ จนกว่าจะเห็นว่าจำเป็นจริงๆ
- SoC – Separation of concerns หลักการออกแบบ software เป็นลักษณะ modular ที่ทำงานใดงานหนึ่งสมบูรณ์ในตัวเอง
- SOLID – Single responsibility, Open/Close, Liskov substitution, Interface segregation, Dependency inversion เป็นหลักการในการเขียน object-oriented programming (OOP) ที่จะทำให้ software มีโครงสร้างที่ดี และแก้ไขได้ง่าย
Single Responsibility Principle – หลักการออกแบบ Class
หลักการที่เรามีแค่เหตุผลเดียวในการสร้าง class ขึ้นมาเพื่อทำงานใดงานหนึ่ง ไม่ควรให้ class ที่สร้างขึ้นมาทำงานหลายหน้าที่ (responsibility) เช่นออกแบบ class เพื่อหาพื้นที่รวมของรูปทรงต่างๆ ก็ไม่ควรให้ class นี้จะต้องทำเรื่องการแสดงผล (output format) ที่ได้ออกมาในรูป html หรือ json เพราะจะทำให้ class นี้ถูกสร้างขึ้นมาด้วยเหตุผลมากกว่าหนึ่งเหตุผล หรือถูกใช้หรือ support เฉพาะบางกลุ่มหรือบาง role
Open Closed Principle – หลักการออกแบบ Interface
Object หรือ entities ต้องสามารถ extend ได้ แต่ต้องแก้ไขไม่ได้ เป็นหลักการที่ทำให้โครงสร้างของ code เดิมไม่กระทบเมื่อมี type หรือ object ที่แตกต่างออกไป เช่น การที่เรามี class สำหรับรวมพื้นรวมของรูปทรงสี่เหลี่ยม กับวงกลม ด้วย method sum() ถ้าเรามีสามเหลี่ยมเพิ่มขึ้นมาก็จะหลีกเลี่ยงการแก้ไข method เดิมไม่ได้ เราสามารถแก้ไขได้ด้วยการสร้าง interface shape โดยให้ type object ใดๆ สามารถ extend ไปเพื่อ implement logic หา area ตัวเอง ก็จะทำให้ mothod sum() ของเราก็ไม่ต้องแก้ไขอะไร เพื่อที่จะ support รูปทรงใหม่ๆ
interface ShapeInterface
{
public function area();
}
class Square implements ShapeInterface
{
// ...
}
class Circle implements ShapeInterface
{
// ...
}
class AreaCalculator
{
// ...
public function sum()
{
foreach ($this->shapes as $shape) {
if (is_a($shape, 'ShapeInterface')) {
$area[] = $shape->area();
continue;
}
throw new AreaCalculatorInvalidShapeException();
}
return array_sum($area);
}
}
Liskov Substitution – หลักการออกแบบ Inheritance
เป็นหลักการที่ object ของ supper class จะต้องสามารถแทนทีด้วย object ของ subclass ได้โดยที่ต้องไม่ส่งผลต่อ program และ object ของ subclass จะต้องสามารถ access ทุก method และ property ของ super class
public interface Bird{
public void fly();
public void walk();
}
public class Parrot implements Bird{
public void fly(){ // to do}
public void walk(){ // to do }
}// ok
public class Penguin implements Bird{
public void fly(){ // to do }
public void walk(){ // to do }
} // it's break the principle of LSP. Penguin can not fly.
จากตัวอย่าง code จะเห็นว่า subclass สามารถ access superclass ได้ แต่จะเห็นว่า penguin ที่เป็น subclass เข้ากันไม่ได้กับ superclass เพราะ penguin ไม่สามารถบินได้จึงผิดหลัก Liskov substitution
public interface Bird{
// to do;
}
public interface FlyingBird extends Bird{
public void fly(){}
}
public interface WalkingBird extends Bird{
public void work(){}
}
public class Parrot implements FlyingBird, WalkingBird {
public void fly(){ // to do}
public void walk(){ // to do }
}
public class Penguin implements WalkingBird{
public void walk(){ // to do }
ถ้าเปลี่ยนใหม่ให้ penguin สือทอดจาก walkingbird ก็จะทำให้ถูกต้องตามหลักการ และไม่ส่งผมต่อ program ทำให้เกิด bug
Interface segregation – หลักการออกแบบ Polymorphism
code จะต้องไม่ถูกบังคับให้ implement หรือเกี่ยวพันกับ method ที่ไม่ได้ใช้ เช่นกรณีที่เรามี class interface ที่มี method คำนวณพื้นที่ และปริมาตรสามมิติ ซึ่งถ้า type ของสี่เหลี่ยมเป็น 2 มิติก็จะไม่สามารถ implement method คำนวณปริมาตรได้เป็นต้น
Dependency Inversion – หลักการออกแบบ Decoupling and Abstraction
หลักการในการจัดการ dependency ของสอง object ที่เมื่อถ้าต้องเปลี่ยนไปใช้ อีก object จะทำได้ง่ายๆ โดยไม่กระทบกับ code ด้วยวิธีการใช้ intermediate object เพื่อเชื่มระหว่างทั้งสอง object เข้าด้วยกัน แทนที่จะให้ทั้งสอง object มีการเรียกใช้กันตรงๆ
public class Book {
void seeReviews() {
...
}
void readSample() {
...
}
}
public class Shelf {
Book book;
void addBook(Book book) {
...
}
void customizeShelf() {
...
}
}
ตัวอย่างนี้ถ้าเราสร้าง shelf ไว้เก็บหนังสือ (book) อนาคตถ้าต้องการเก็บ DVD ด้วยก็เลี่ยงไม่ได้ที่ต้องแก้ class shelf เพราะความสัมพันธ์ที่ขั้นตรงต่อกันของทั้งสอง object
public interface Product {
void seeReviews();
void getSample();
}
public class Book implements Product {
@Override
public void seeReviews() {
...
}
@Override
public void getSample() {
...
}
}
public class DVD implements Product {
@Override
public void seeReviews() {
...
}
@Override
public void getSample() {
...
}
}
public class Shelf {
Product product;
void addProduct(Product product) {
...
}
void customizeShelf() {
...
}
}
ถ้าไม่ต้องการให้ object มี dependency ต่อกันก็ต้องสร้าง intermediate object ขึ้นมา ตัวอย่างนี้คือ object product ซึ่งเป็น abstraction ของทั้งสอง object ทำให้อนาคตถ้ามี object ใหม่ๆ เกิดขึ้น ก็สามารถเพิ่มเข้า หรือลบออกได้ง่าย
บทความที่น่าสนใจ และศึกษาเพิ่มเติมเพื่อให้เข้าใจมากขึ้นสำหรับ architecture และ software pattern อื่นๆ
Architecture
- Azure Design Patterns
- Microservice Architecture and Design Patterns for Microservices – Medium
- Netflix Architecture – Medium
- Ready for changes with Hexagonal Architecture – Medium
- Deciphering the difference between a service mesh and api-gateway – Medium
Design Patterns
- Rapid Modernization of Java Applications: Practical Business and Technical Solutions for Upgrading Your Enterprise Portfolio – G. Venkat
- Microservices Antipatterns and Pitfalls – Mark Richards
- Building a reporting service in Microservice architecture – Medium
- Handling Distributed Transactions in the Microservice world – Medium
- Aggregation pattern and SCG
- Beyond the Twelve Factor App – VMWare Blog
- Strangling a Monolith with a data driven approach – VMware Blog
- Circuit Breaker in microservices – Medium
- Circuit Breaker – Martin Flower
- Spring Cloud Circuit Breaker
- Observer vs Pub/Sub Pattern – Medium
- 12factor app
- Maintaining resilience in microservices architectures – Medium
- Cache is the route of all Evil
- The role of service mesh and API Gateway in microservices architectures
Chaos Engineering
