Giter VIP home page Giter VIP logo

designpattern's Introduction

Hello World

by: Vietnamese

I'm Anh, from 🇻🇳 and be a 👨‍💻, like to build a system by DDD.

Currently have interested in System Design 👷 🏗️ and Database.

Github statistic

GitHub stats

Tech Toolbox 🧰

Database 🗃️

MySQL MongoDB

Infrastructure

AWS

Programming Language 💻

JS NodeJS TS Dart Python

UI 🎨

React Redux MUI

Mobile 📱

Flutter

ORM ⚙️

Sequelize

Tool ⚒️

Git

Testing 🧪

Jest

Deep Learning 🤖

Tensorflow

Architecture 🏗️

Clean architeture + DDD

CQRS, Event Sourcing Screen Shot 2023-09-16 at 13 32 19

designpattern's People

Contributors

tuananhhedspibk avatar

Stargazers

 avatar

Watchers

 avatar

designpattern's Issues

Singleton Pattern

Singleton Pattern cho phép bạn có thể chắc chắn rằng class chỉ có duy nhất một instance song song với đó, nó sẽ cung cấp glbal access point đến instance đó.

Vấn đề

  1. Đảm bảo class chỉ có duy nhất một instance: Tại sao ta cần phải kiểm soát số lượng các instance của một class. Câu trả lời đó là khi instance của class là tài nguyên dùng chung (DB hoặc file)

Đây là cách nó hoạt động: hãy tưởng tượng bạn vừa tạo một object nhưng sau đó, khi bạn quyết định tạo thêm một object khác nữa, thay vì nhận về một object mới tinh bạn chỉ nhận về được object mà bạn đã có trước đó.

Chú ý rằng điều này là không thể với các constructor thông thường vì các constructor thông thường sẽ luôn trả về một object mới tinh cho chúng ta

  1. Cho phép global access tới instance: Các biến global rất tiện lợi, tuy nhiên nó cũng tiềm ẩn những nguy cơ nhất định như việc bất kì đoạn code nào có thể truy cập tới nó, thay đổi giá trị của nó từ đó dẫn tới khả năng crash app.

Cũng tương tự như các biến toàn cục, Singleton pattern cho phép chúng ta có thể truy cập đến các objects ở bất cứ đâu trong chương trình. Tuy nhiên nó cũng bảo vệ các objects đó khỏi việc bị ghi đè bởi các đoạn code khác.

Có một vấn đề khác mà chúng ta cần quan tâm ở đây đó là việc chúng ta không hề muốn các đoạn code giải quyết vấn đề thứ nhất (Đảm bảo class chỉ có duy nhất một instance) nằm rải rác ở nhiều nơi trong chương trình của chúng ta, tốt hơn hết là giữ nó ở trong một class duy nhất (nhất là khi code của bạn phụ thuộc nhiều vào nó)

Giải pháp

Có 2 bước

  1. Thiết lập sao cho constructor trở thành private để tránh việc sử dụng new operator ở nhiều chỗ khác nhau
  2. Tạo một static creation method hoạt động với vai trò như constructor nhưng thực chất là nó sẽ gọi tới private constructor đã có trước đó, lưu instance mới tạo vào một static field. Những lần gọi hàm creation sau đó đều trả về cached object.

Thực tế

Chính phủ của một đất nước là một ví dụ điển hình về Singleton pattern. Mỗi quốc gia chỉ có một chính phủ duy nhất. Tên gọi "Chính phủ nước ..." giống như một con trỏ, trỏ tới một nhóm người nhất định đang làm việc cho chính phủ của một quốc gia.

Cấu trúc

Screen Shot 2022-02-27 at 16 05 22

Singleton class với static method là getInstance sẽ tạo mới instance nếu nó chưa tồn tại còn nếu nó đã tồn tại thì sẽ luôn trả về instance hiện có chứ không hề tạo mới instance.

Tính ứng dụng

  1. Sử dụng Singleton Pattern khi class chỉ nên có duy nhất một instance được sử dụng bởi mọi clients (VD: DB object)
  2. Sử dụng Singleton Pattern khi bạn muốn kiểm soát nghiêm ngặt các biến global.

Nhược điểm

Đáng kể nhất đó là khi viết unit test cho Singleton vì ta cần tạo mock object. Do constructor của Singleton là private cũng như ta không thể ghi đè được static method (với nhiều ngôn ngữ hiện có) thì việc tạo mock object cần phải áp dụng một cách làm mới. Hoặc nếu không bạn có thể không viết test hoặc không dùng Singleton Pattern.

Adapter Pattern

Adapter Pattern cho phép các objects với interface khác nhau có thể hoạt động với nhau.

Vấn đề

Hãy tưởng tượng bạn đang phát triển một app theo dõi giá cổ phiếu. App này download dữ liệu cổ phiếu từ nhiều nguồn với XML format, hiển thị nó dưới dạng đồ thị. Thế nhưng khi bạn muốn tích hợp thêm một thư viện analytics khác vào thì thư viện này lại sử dụng JSON format.

Bạn hoàn toàn có thể sửa thư viện để nó có thể làm việc với XML format, tuy nhiên điều này sẽ làm ảnh hưởng đến code hiện có của thư viện cũng như có những trường hợp bạn không thể truy cập vào source code của thư viện nên cách tiếp cận này là không thể.

Giải pháp

Bạn có thể tạo ra một adapter. Đây là một object đặc biệt, nó convert interface của một object sang dạng mà object khác có thể hiểu được.

Một Adapter có thể wrap lấy một object để che dấu đi sự chuyển đổi kiểu dữ liệu phức tạp ở phía sau. Ví dụ bạn có thể wrap một object làm việc với đơn vị (m, km) để nó có thể xử lí với các đơn vị như (feet hoặc miles). Các object được wrap lại này không nhất thiết phải biết về adapter.

Adapter không những đáp ứng được việc chuyển đổi format dữ liệu mà nó còn giúp các objects với interface khác nhau có thể làm việc được với nhau. Cơ chế như sau:

  1. Adapter nhận về một interface, tương thích với một object đang tồn tại.
  2. Sử dụng interface này, object có thể gọi các adapter's method.
  3. Khi nhận được lời gọi, adapter sẽ chuyển request đến cho object thứ 2 nhưng dưới format và thứ tự mà object này mong muốn.

Có những trường hợp ta hoàn toàn có thể tạo ra two-way adapter có thể convert lời gọi theo 2 chiều.
Dưới đây là mô hình cho app tracking stock ở phía trên.

Screen Shot 2022-03-12 at 16 10 31

Cấu trúc

Object adapter
Adapter sẽ implement interface của một object và wrap các objects còn lại.

Screen Shot 2022-03-12 at 17 00 54

  1. Client là class bao hàm business logic
  2. Client Interface mô tả protocol mà các classes khác phải tuân thủ khi làm việc với Client code.
  3. Service thường là các thư viện bên ngoài, hoặc các code đã có từ trước. Client không thể dùng nó trực tiếp do interface bị xung đột.
  4. Adapter là class có khả năng làm việc với cả client và service. Nó triển khai client interface vào wrap service object. Adapter nhận lời gọi từ phía client, chuyển chúng thành các lời gọi theo format mà service object có thể hiểu được.
  5. Client không cần quan tâm đến sự tồn tại của adapter. Nhờ điều này việc đưa các adapters mới vào chương trình sẽ không làm ảnh hưởng đến client code. Điều này cũng có lợi khi interface của service class thay đổi thì việc đưa một adapter mới vào chương trình cũng không làm ảnh hưởng gì đến client code.

Class adapter

Cách làm này cho phép Adapter class kế thừa trực tiếp từ Client Class và Service Class (điều này chỉ được phép với C++)

Screen Shot 2022-03-12 at 17 08 28

  1. Class Adapter không cần phải wrap bất kì object nào vì nó kế thừa từ client và service. Quá trình adaptation sẽ diễn ra trong method được ghi đè.

Tính ứng dụng

Nó cho phép ta có thể tạo ra các middle-layer class làm việc như một translator giữa code hiện tại và các code sẵn có cũng như các thư việc từ bên ngoài hoặc các class với interface kì cục khác.

Builder

Cho phép chúng ta có thể tạo ra các objects phức tạp theo từng bước một. Pattern cũng cho phép chúng ta có thể tạo các kiểu objects khác nhau sử dụng cùng một source code.

Vấn đề

Hãy tưởng tượng việc chúng ta phải viết các constructor code dài ngoằng, với nhiều dòng code và tham số. Đây là một điều vô cùng tồi tệ khi bạn phải maintain mớ bòng bong đó.

Screen Shot 2022-01-11 at 15 31 15

Ví dụ thực tế là khi xây nhà. Bạn cần

  • Xây tường
  • Xây sàn
  • Xây mái
  • ...

Nhưng nếu bạn muốn ngôi nhà của mình to hơn, sáng hơn với sân sau hoặc hệ thống sưởi cho mùa đông ?
Giải pháp đơn giản nhất đó là kế thừa từ House class, sau đó tạo các subclasses để giải quyết hết các nhu cầu trên. Tuy nhiên điều mà bạn nhận lại được đó là một đống các subclasses đi kèm. Cứ khi nào có parameter mới là một lần bạn phải tạo thêm một subclass mới nữa.

Một giải pháp khác nữa đó là tạo một constructor khổng lồ, đáp ứng mọi parameters cho House base class. Cách làm này giúp giảm đi đáng kể số lượng các subclasses, nhưng nó lại tạo ra thêm các vấn đề khác đi kèm.

Screen Shot 2022-01-11 at 15 53 37

Đa phần trong mọi trường hợp, chúng ta sẽ không sử dụng mọi parameters. Điều này vô tình dẫn đến việc các lời gọi hàm trông sẽ rất xấu.

Giải pháp

Tách construction code ra khỏi class và đưa nó đến các objects khác gọi là các builders.

Screen Shot 2022-01-11 at 15 56 20

Pattern này sẽ tổ chức quá trình tạo object thành các bước (buildWalls, buildDoor, ...). Để tạo ra object bạn chỉ cần gọi đến các hàm mà mình cần (tương ứng với đặc tả object mà bạn mong muốn).

Có những lúc các bước xây dựng object sẽ được implement theo những cách khác nhau (với cabin thì buildWalls sẽ sử dụng gỗ, còn với nhà thông thường buildWalls sẽ sử dụng gạch).

Trong những trường hợp như vậy bạn có thể tạo các classes khác nhau để triển khai tập hợp các builder methods theo những cách khác nhau. Khi đó quá trình tạo một object sẽ là tập hợp lời gọi các builder methods.

Screen Shot 2022-01-11 at 16 04 06

Director

Director Class định nghĩa các bước để thực thi building steps, trong khi builder sẽ cung cấp implementation cho các bước này.
Phía client không nhất thiết phải sử dụng Director Class mà hoàn toàn có thể gọi các builder methods theo trình tự mà mình mong muốn.

Tuy nhiên Director Class có thể là nơi đặt các construction routines để bạn có thể tái sử dụng chúng. Ngoài ra thì Director Class cũng giúp chúng ta có thể che dấu đi các bước tạo ra object, client chỉ đơn thuần gọi đến Director Class và lấy về object mong muốn.

Cấu trúc

Screen Shot 2022-01-11 at 16 22 36

  1. Builder interface
  2. Các class implement Builder interface
  3. Products: các result object
  4. Client phải truyền Builder vào cho Director

Dependency Inversion Principle

Các high level class không nên phụ thuộc vào low level class. Cả hai chỉ nên phụ thuộc vào các yếu tố trừu tượng (abstraction). Abstractions không nên phụ thuộc vào detail. Details nên phụ thuộc vào Abstractions.

Khi thiết kế phần mềm, ta thường chia các classes thành 2 nhóm như dưới đây:

  • Low level classes: triển khai các basic implementation như: transferring data hoặc kết nối với network, làm việc trực tiếp với DB
  • High level classes: triển khai các business logic phức tạp, cũng như chỉ định các low level classes làm việc cho mình.

Thường thì chúng ta sẽ bắt đầu với các low-level classes rồi mới đi lên high-level classes. Các tiếp cận này thường diễn ra khi chúng ta bắt đầu triển khai prototype cho project và lúc đó chúng ta cũng chưa chắc chắn rằng các high-level classes sẽ có những logic gì ở bên trong do low level class chưa được triển khai.

Với cách tiếp cận như thế này thì business logic sẽ bị phụ thuộc vào các primitive low-level classes.

Dependency Inversion principle đưa ra các tiếp cận ngược lại so với cách tiếp cận ở phía trên

  1. Đầu tiên, ta cần định nghĩa các interfaces cho các low-level operations mà high-level classes sẽ sử dụng. VD: business logic chỉ nên gọi openReport(file) thay vì một chuỗi các methods openFile(x), readBytes(n), closeFile(x). Các interfaces này được coi như thuộc về high-level.
  2. Và bây giờ high-level classes chỉ phụ thuộc vào các interfaces thay vì các low-level classes (sự phụ thuộc này mềm mại và uyển chuyển hơn nhiều so với lúc đầu).
  3. Khi các low-level classes implement các interfaces này, chúng sẽ phụ thuộc vào business logic level, ngược lại hoàn toàn so với hướng phụ thuộc lúc ban đầu

Dependency inversion thường đi kèm với open/closed principle. Khi đó ta có thể extend low-level classes để sử dụng cho các business logic khác thay vì sửa trực tiếp các classes hiện có.

VD
Screen Shot 2022-06-04 at 12 26 03

Ta thấy rằng BudgetReport class sẽ phụ thuộc vào low-level classes (loại DB nào).

Để giải quyết vần đề này ta sẽ tạo một high-level interface để high-level class sử dụng nó. Khi đó các low-level classes có thể có nhiều cách triển khai khác nhau tuỳ theo business logic (liên quan đến chuyện lưu trữ hay lấy về dữ liệu)

Screen Shot 2022-06-04 at 12 27 05

Lúc này ta thấy low-level classes sẽ phụ thuộc vào high-level classes.

Interface Segregation Principle

Client không nên bị ép sao cho phụ thuộc vào các methods mà client không sử dụng

Hãy làm cho interfaces của bạn nhỏ nhất, hẹp nhất có thể để client không nhất thiết phải implement những methods mà nó không sử dụng.

Hãy chia nhỏ "fat interface" thành các interfaces nhỏ nhất và cụ thể nhất có thể

Client chỉ cần implement những methods mà client thực sự cần.

VD: Hãy thử tưởng tượng ban đang làm một thư viện giúp tích hợp apps với nhiều loại cloud computing providers. Ban đầu bạn chỉ triển khai với AWS, tuy nhiên khi số lượng cloud providers tăng lên sẽ xảy ra TH một số interface của loại cloud này không có các methods mà loại cloud kia triển khai.

Screen Shot 2022-06-04 at 23 06 19

Cách giải quyết ở đây vẫn là chia nhỏ interface ra thành nhiều interfaces khác nhau, mỗi "interface con" này sẽ chỉ chứa những method thực sự liên quan đến chức năng của nó.

Screen Shot 2022-06-04 at 23 09 25

Tuy nhiên khi interface đã đạt được một mức độ cụ thể nhất định, ta cũng không nhất thiết phải chia nhỏ nó ra thêm nữa vì càng nhiều interfaces thì code sẽ càng trở nên phức tạp hơn. Hãy giữ sự cân bằng.

Abstract Factory

Cho phép ta có thể tạo một tập các objects liên quan đến nhau mà không cần phải thông qua các classes cụ thể nào.

Vấn đề

Giả sử bạn đang dev một shop simulator về đồ nội thất, code của bạn gồm các classes:

  1. Các đồ dùng gia đình: Chair + Sofa + Coffee Table
  2. Các kiểu dáng: Modern, Victorian, ArtDeco

Screen Shot 2022-01-03 at 21 18 16

Bạn muốn bán cho khách hàng đúng loại đồ nội thất mà họ muốn, đồng thời đáp ứng kịp thời cho sự thay đổi catalog của nhà cung cấp mà không cần phải thay đổi core code.

Giải pháp

Abstract Factory sẽ xử lí vấn đề bằng cách tạo ra các interfaces cho các đồ dùng (VD: Chair, Sofa, Coffee Table). Sau đó các kiểu dáng sẽ là các class implements các interfaces trên.

Screen Shot 2022-01-03 at 21 23 50

Tiếp theo đó là tạo Abstract Factory - bản chất là một interface với các methods tạo ra các đồ nội thất Chair, Sofa, ... tương ứng là createChair, createSofa. Các methods này sẽ trả về các abstract product type mà ta đã định nghĩa thông qua các interface Chair, Sofa, ...

Với các kiểu dáng, ta sẽ tạo ra các Factory class implement Abstract Factory ở trên, với các methods sẽ trả về (ModernChair, ModernSofa tương ứng với createChair()createSofa()).

Screen Shot 2022-01-03 at 21 34 44

Screen Shot 2022-01-03 at 21 37 52

Do phía client không quan tâm đến class cụ thể của factory, nên ta có thể truyền xuống cho client code (kiểu dáng cũng như factory type).

Phía client sẽ xử lí các loại Chair như VictorianChair hoặc ModernChair như nhau vì đều là Chair (abstract interface). Với cách tiếp cận này thì client chỉ có thể biết về method sitOn() của Chair mà thôi. Hơn nữa SofaCoffeeTable được trả về sẽ luôn đồng loại với Chair.

Còn một vấn đề nữa đó là khi client chỉ sử dụng interface của factory thôi thì sao ? Khi đó factory object sẽ được khởi tạo từ ban đầu, bản thân factory type sẽ được lấy từ các biến môi trường hoặc các biến config.

Cấu trúc

Screen Shot 2022-01-03 at 21 44 14

  1. Abstract Product: các interfaces định nghĩa các product liên quan.
  2. Concrete Product: triển khai các abstract products nhóm theo các kiểu dáng. Mỗi abstract product phải được triển khai toàn bộ các kiểu dáng Modern, ArtDeco, ...
  3. Abstract Factory: interface định nghĩa các methods tạo các Abstract Product.
  4. Concrete Factory: triển khai Abstract Factory, với các methods tạo ra các products thuộc cùng một kiểu dáng.
  5. Concrete Factory tạo ra các Concrete Product xong chữ kí các hàm creation của nó sẽ phải trả về Abstract Product

Single Responsibility Principle

Một class chỉ nên có duy nhất một lí do để thay đổi.

Một class chỉ nên đảm nhận một chức năng duy nhất. Tránh việc tạo ra một class với nhiều chức năng trong nó. Khi đó việc thay đổi class để đáp ứng với requirement sẽ làm ảnh hưởng đến những phần khác của class mà ta không chủ tâm muốn thay đổi chúng.

Bạn không nhất thiết phải áp dụng một design hoàn hảo cho 200 dòng codes. Chỉ cần chia hàm là ổn.

VD:
Chúng ta hoàn toàn có đủ lí do để chia class Employee dưới đây thành các subclasses.

Screen Shot 2022-01-11 at 17 06 27

Bản chất của việc sử dụng class Employee là để quản lí nhân viên, nhưng report của nhân viên sẽ thay đổi theo thời gian. Nên ta sẽ tách class Employee thành 2 class: EmployeeTimeSheetReport.

Screen Shot 2022-01-11 at 17 08 53

Dependency Injection

Thế nào là dependencies ?

Lấy ví dụ với hàm sau:

const randomInRange = (min: number, max: number): number => {
  return Math.random() * (max - min) + min;
}

Hàm này rõ ràng phụ thuộc vào hàm thư viện Math.random(), nếu trường hợp hàm này không hoạt động thì hàm randomInRange cũng sẽ không hoạt động theo. Do đó Math.random() là một dependency.

Sửa lại hàm một chút

const randomInRange = (min: number, max: number, random: () => number = Math.random) => {
  return random() * (max - min) + min;
}

Ta thấy rằng hàm randomInRange không còn phụ thuộc vào Math.random nữa, ngoài ra ta cũng có thể tuỳ ý sử dụng bất kì một hàm random có sẵn tuỳ ý muốn. Đây chính là cách triển khai nguyên thuỷ nhất của dependency inversion

Ta sẽ truyền vào module mọi dependencies mà nó cần

Lí do

Có 2 lí do chính:

  • Testability: khi chỉ rang tường minh dependency nào sẽ được sử dụng, ta có thể biết được tầm ảnh hưởng của nó đến đối tượng test đề từ đó có thể mock lại dependency đó. Với hàm getRandomInRange ở phía trên ta không thể test được nếu không mock do nó là hàm random. Code test chông sẽ như sau:
const randomMock = () => return 0.1;

const res = getRandomInRange(1, 2, randomMock);

assert.equal(res, 1.1);
  • Thay thế các dependencies cho nhau: trong trường hợp dependency hiện thời bị lỗi hoặc bị khai tử, ta có thể thay thế nó bởi một dependency khác vẫn phù hợp với yêu cầu của module.
const otherRandom = (): number => {
 // implementation
}

const getRandomInRange = (min: number, max: number, otherRandom): number => {
 // ...
}

Dependencies on Abstraction

Khi định nghĩa các behaviour trong thực tế, ta sẽ đi theo flow sau:

  1. Định nghĩa abstract convention
  2. Dựa theo convention phía trên ta định nghĩa các modules cũng như các adapters để sử dụng với external libraries.

Dependency Injection

class Counter {
  public state: number = 0;

  public increase(): void {
    this.state += 1;
    console.log(`State increased. Current state is ${this.state}.`);
  }

  public decrease(): void {
    this.state -= 1;
    console.log(`State decreased. Current state is ${this.state}.`);
  }
}

Ở đoạn code trên ta thấy class phụ thuộc vào dependency là console. Nếu ta:

  • Truyền console vào các method dưới dạng params
    Hoặc
  • Truyền vào class thông qua constructor

thì vấn đề phụ thuộc vào dependency sẽ được giải quyết

interface Logger {
  log: (message: string) => void
}

class Counter {
  constructor(
    private logger: Logger,
  ) {}

  public state: number = 0;

  public increase(): void {
    this.state += 1;
    this.logger.log(`State increased. Current state is ${this.state}.`);
  }

  public decrease(): void {
    this.state -= 1;
    this.logger.log(`State decreased. Current state is ${this.state}.`);
  }
}

const instance = new Counter(console);

Ngoài console ta cũng có thể truyền các dependencies khác miễn sao nó implement Logger interface là được.

const alertLogger: Logger = {
  log: (message: string): void => {
    alert(message);
  },
};

const counter = new Counter(alertLogger);

Automatic Injections and DI Containers

Việc inject các dependencies vào các modules đang được thực hiện bằng tay. Tuy nhiên ta có thể triển khai tự động bằng DI Containers.

  • Container sẽ biết được module nào cần những dependencies gì để khi module cần thì dependencies sẽ được tạo và inject vào module.
  • Lúc này module sẽ không cần phải quan tâm đến các dependencies nó cần đang nằm ở đâu và inject vào như thết nào nữa (S, D trong SOLID principle)

Về mặt định nghĩa DI Containers có chức năng cung cấp (provide) các dependencies cho các modules.

SINGLETON scope: Sau khi instance được tạo ra, nó sẽ được lưu vào cache.

Do mọi thứ sẽ được container đảm nhận nên ta không cần thiết phải truyền dependencies bằng tay.

Phía dưới là DI của NestJS

_

Reference from this

Decorator Pattern

Problem

Giả sử bạn có một class Notifier với method send, method này chỉ gửi message đi mà thôi, thế nhưng bạn còn muốn nhiều hơn thế, thay vì chỉ gửi message bạn còn muốn gửi

  • Mail
  • Tin nhắn slack
  • Tin nhắn facebook
  • ...

Một cách giải quyết đơn giản đó là extends từ class Notifier thành các classes:

  • FacebookNotifier
  • SlackNotifier
  • ...

Thế nhưng cách làm này có một nhược điểm có thể thấy rõ đó là việc nó sẽ làm tăng số lượng classes lên một cách "đáng kể".

Solution

  1. Việc kế thừa không giúp chúng ta có thể thay đổi hoặc chỉnh sửa behavior của obj tại runtime. Mà ta chỉ có thể thay thế toàn bộ object mà thôi.
  2. Thông thường các ngôn ngữ OOP chỉ cho phép đơn kế thừa (1 subclass extend 1 parentClass only).

Một cách giải quyết ở đây đó là Aggregation hoặc Composition, bản thân 1 object sẽ kết tập từ nhiều objects thuộc về các classes khác, từ đó "chuyển tiếp - delegate" công việc cho các classes kia.

Screenshot 2023-12-27 at 23 11 41

Có thể mô tả pattern này trong 1 từ đó là "Wrapper", khái niệm wrapper ở đây sẽ được hiểu như việc một object có thể "link" tới các target objects khác. Wrapper thường delegate các requests mà nó nhận được (trong thực tế thì wrapper có thể chỉnh sửa kết quả hoặc param sau hoặc trước khi delegate cho các target objects).

Bản thân các wrapper cũng sẽ implements các interface giống như các objects được "bao" bên trong wrapper, từ phương diện của client thì các objects này là như nhau.

Các decorator sẽ bao lấy nhau thành 1 stack, stack cuối cùng sẽ được sử dụng bởi client. Do các decorator đều implement 1 interface nên client code sẽ không quan tâm tới việc đó là "pure" object hay là một decorator.

Screenshot 2023-12-28 at 22 46 54

Screenshot 2023-12-28 at 23 00 04

Structure

Screenshot 2023-12-28 at 23 01 22

Pros

  1. Có thể mở rộng object behavior mà không cần tạo subclass
  2. Có thể thêm chức năng cho object ở runtime
  3. Có thể kết hợp các behaviors lại bằng cách wrap một object với nhiều decorators.

Cons

  1. Khó có thể loại bỏ một wrapper khỏi wrapper stack.
  2. Khó có thể triển khai một decorator theo cách mà các behaviors của nó không phụ thuộc vào thứ tự của decorators stack.

Prototype Pattern

creational pattern cho phép ta có thể copy object mà không cần phải phụ thuộc vào class của nó.

Vấn đề

Bạn muốn copy một object, điều đầu tiên cần làm đó là tạo một instance mới của class, sau đò duyệt qua toàn bộ các giá trị của các fields thuộc về object hiện có và gán giá trị đó cho object mới tạo. Cách làm này không sai nhưng không thể áp dụng khi object có những private fields.

Ngoài ra còn có một vấn đề khác đó là bạn cần phải biết class tương ứng với object vừa tạo, điều này khiến code của bạn sẽ phụ thuộc vào một class. Hơn nữa có thể bạn chỉ biết mỗi interface của object chứ không biết được class cụ thể của nó.

Giải pháp

Prototype Pattern sẽ uỷ thác việc clone object cho chính object được clone. Cụ thể là bạn có thể định nghĩa một interface chung cho các object mà bạn muốn clone. Đơn giản chỉ là định nghĩa interface với method clone.

Việc triển khai clone method là hoàn toàn giống nhau ở các class. Cụ thể là tạo một object mới, sao lưu toàn bộ giá trị của các fields vào object mới. Bản thân mọi ngôn ngữ lập trình cũng cho phép các object có thể truy cập đến các private field của object khác nên ta hoàn toàn có thể sao lưu các giá trị private một cách dễ dàng.

Một object hỗ trợ cloning sẽ được gọi là prototype.

Khi object của bạn có nhiều fields cũng như các config khác thì việc clone chúng có thể được triển khai ở subclass.

Implementation

Screen Shot 2022-02-27 at 12 31 18

(1) Định nghĩa Prototype interface với clone method
(2) ConcretePrototype class sẽ implement Prototype interface, ngoài việc copy object gốc, clone method có thể có các xử lí khác nếu object cần clone là nested object.

Open/Closed Principle

Class nên được mở rộng thay vì được sửa đổi trực tiếp

Mục đích chính của nguyên tắc này đó là giữ cho code được toàn vẹn khi thêm tính năng mới.

Một class được gọi là mở nếu bạn có thể dễ dàng kế thừa, tạo sub-class, thêm các methods hoặc fields mới.
Khi class không thể mở rộng được nữa (có thể được sử dụng bởi các classes khác, interface của nó đã hoàn thiện và sẽ không thay đổi trong tương lai) thì class sẽ được coi là đóng.

Tuy nhiên một class có thể được coi là mở (cho việc mở rộng) và đóng (với việc chỉnh sửa) cùng 1 lúc.

Khi class đã được đưa vào framework và được sử dụng rộng rãi thì việc thay đổi source code của class là một việc làm không cần thiết, thay vào đó bạn có thể kế thừa, tạo subclasses từ class đó.

Trên thực tế, nếu một class bị bug, bạn nên sửa trực tiếp nó thay vì tạo ra subclass như một bản vá cho class hiện thời vì subclass không có nhiệm vụ là giải quyết issues của parent-class.

VD:
Bạn có một ứng dụng EC, bạn muốn thay đổi công thức tính phí shipping. Bạn hoàn toàn có thể thay đổi trực tiếp class Order như ở dưới đây.

Screen Shot 2022-01-11 at 17 38 53

Thế nhưng việc thay đổi class code như vậy có thể làm ảnh hưởng đến các phần khác của ứng dụng, thay vào đó bạn có thể ứng dụng Strategy Pattern, đưa getShippingCost() method sang một class khác có chung interface.

Screen Shot 2022-01-11 at 17 42 50

Lúc này khi bạn muốn thêm một shipping method khác, bạn chỉ cần tạo một class mới implement Shipping Interface mà không cần phải đụng chạm gì đến Order class. Client code sử dụng Order class chỉ cần link Order Class với Shipping Object mới là xong.

Client Side Rendering

Trong Client Side Rendering (CSR), chỉ có phần khung HTML được render bởi phía server ngoài ra:

  • Logic
  • Data Fetching
  • Templating
  • Routing
    được xử lí ở phía client.

Đây như một phương thức giúp triển khai các SPA (Single Page Application), xoá nhoà đi sự khác biệt giữa website và installed application.

Basic structure

Cùng xét ví dụ hiển thị thời gian đơn giản như sau:

<div id="root"></div>
function tick() {
  const element = (
    <div>
      <h1>Hello World</h1>
      <h2>It is {new Date().toLocaleTimeString()}</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);

Code HTML chỉ gồm một thẻ div duy nhất. Phần code hiển thị, xử lí thời gian đều nằm ở phía JS (TS), mọi thứ được update ngay lập tức mà không cần phải thông qua server hay API nào cả.

JavaScript bundles và Performance

Đi kèm theo sự phức tạp của các trang web như:

  • Hiển thị ảnh
  • Hiển thị dữ liệu từ data store
  • Event handling
    thì kích thước của JS code cũng sẽ tăng theo.

Việc render một trang với file bundle có kích thước lớn sẽ làm tăng FCP và TTI của page.

Screen Shot 2022-03-27 at 16 14 48

Như hình minh hoạ phía trên, khi việc render file bundle càng mất thời gian thì khoảng cách giữa FP với FCPTTI càng tăng, đồng nghĩa với việc khoảng thời gian mà người dùng nhìn thấy một trang trắng cũng sẽ tăng theo.

Client-side React - Ưu và nhược điểm

Mọi ứng dụng được dev bởi React đều thực thi ở phía client, nó sẽ tương tác với API để lấy về data hoặc lưu data. Việc bạn nhấn vào 1 link hoặc thay đổi route không làm phát sinh bất kì một req nào về phía server, mọi xử lí sẽ nằm ở phía client.

CSR cho phép SPA có thể navigate mà không cần reload page - Đây là một trải nghiệm tuyệt vời đối với người dùng. Qua đó khiến cho web trở nên responsive hơn.

Bên cạnh những ưu điểm trên, CSR cũng có một số nhược điểm sau:

  1. SEO: các web crawler có thể dễ dàng thông dịch được các web được render bởi server. Với CSR mọi thứ sẽ phức tạp hơn khi phải render một trang lớn với các network req đi kèm, sẽ khó cho crawler có thể index page do việc render page sẽ lâu hơn.
  2. Performance: CSR cải thiện trải nghiệm của người dùng khi không phải load đi load lại trang quá nhiều nhưng ở thời điểm load page đầu tiên, người dùng có thể phải chờ một khoảng thời gian đáng kể nếu kích thước file bundle lớn.
  3. Khả năng maintain code: sẽ có nhiều component bị lặp lại ở cả phía client và server (VD như validate, format logic...)
  4. Data fetching: với CSR, việc fetching data sẽ thiên về event-driven (hướng sự kiện) - sẽ phát sinh khi load page hoặc click button.

Improving CSR performance

Vấn đề về hiệu năng với CSR thường sẽ nằm ở file bundle. Để cải thiện điều này chỉ có một cách duy nhất đó là tối ưu hoá code.

  • *Budgeting Javascript: giới hạn kích thước file bundle < 100-170KB minified & gzipped sẽ là một điểm cộng
  • Preloading: kĩ thuật này dùng để load các tài nguyên dùng chung trước khi page được render. Các tài nguyên dùng chung này có thể preload bằng việc chèn thẻ dưới đầy vào <head> trong code HTML
<link rel="preload" as="script" href="critical.js">

phần code phía trên sẽ load file critical.js trước khi cơ chế render page được thực hiện. Tuy nhiên nó lại không hề block lại việc render page.

  • Lazy loading: ta sẽ phân ra rõ ràng các tài nguyên dùng chung và không dùng chung. Các tài nguyên không dùng chung sẽ chỉ được load khi cần thiết. Lúc này ở lần load page đầu tiên ta sẽ chỉ load những tài nguyên thực sự cần thiết. Ví dụ như Chat component sẽ không nhất thiết phải load ở lần load đầu tiên
  • Code Splitting: ta sẽ chia nhỏ file bundle ra để tiến hành dynamic loading. Việc này có thể thực hiện bới các bundlers như Webpack.
  • Application shell caching with service workers: Sử dụng service worker để cache phần HTML, CSS, JS phục vụ cho việc render UI.

Ref: https://www.patterns.dev/posts/client-side-rendering/

Bridge Pattern

Là Pattern cho phép ta chia một class lớn hoặc tập các classes có mối liên hệ gần nhau thành 2 classes, tập riêng biệt, độc lập với nhau.

Vấn đề

Ta cùng nhau lấy một ví dụ với class tổng quát là Shape với 2 sub-classes là CircleSquare. Giả sử trong trường hợp ta muốn kết hợp với màu, ví dụ như: Red & Blue thì ta cần cả thảy 4 sub-classes: BlueCircle, BlueSquare, RedCircle, RedSquare.

Screen Shot 2022-03-12 at 21 14 05

Nếu sau này ta thêm một shape mới như Triangle thì ta cần thêm 2 sub-classes đó là BlueTriangle, RedTriangle. Sau đó nếu thêm màu mới thì ta lại phải thêm 3 sub-classes nữa.

Tổng quát lên, nếu số lượng shape và màu tăng thì số lượng sub-classes sẽ tăng với tốc độ của hàm mũ.

Giải pháp

Vấn đề này xảy ra là do ta đang cố gắng mở rộng class Shape thành 2 nhánh là colorform. Đây là vấn đề thường gặp với class inheritance.

Bridge pattern giải quyết vấn đề này bằng cách thay vì kế thừa sẽ chuyển qua object composition. Tách một nhánh cũ thành một tập class và sub-classes riêng biệt. Class gốc ban đầu sẽ tham chiếu đến class thuộc về nhánh mới này thay vì giữ toàn bộ thuộc tính và behavior của nó trong một class duy nhất.

Screen Shot 2022-03-12 at 21 14 10

Lúc này, việc thêm màu mới cho Shape không quá khó khăn khi chỉ cần thêm vào list các colors của Shape mà thôi. Reference tới Color thuộc Shape sẽ đóng vai trò như một chiếc cầu nối giữa 2 classes.

Abstraction & Implementation

Abstraction là tầng ở mức cao, nó không thực hiện một hành động thực tế nào cả, thay vào đó nó sẽ chuyển tiếp công việc này cho tầng implementation (còn gọi là tầng platform).

Khi nói về ứng dụng trong thực tế thì những gì được gọi là abstraction thường sẽ được cụ thể hoá thông qua UI. Và việc triển khai sẽ nằm ở API hoặc OS.

Nói một cách tổng quát, ta có thể chia một ứng dụng trong thực tế ra thành 2 nhánh độc lập:

  • Có một vài UIs (giao diện cho admin và user thông thường)
  • Hỗ trợ một vài APIs khác biệt (app có thể chạy ở Window, MacOS, Linux)

Tuy nhiên trong kịch bản tồi tệ nhất, app có thể trông như một bát mì Ý khổng lồ khi cả tá điều kiện kết nối giữa UI và API tồn tại trong code của bạn.

Việc sửa đổi một app với kiến trúc là monolithic sẽ rất khó vì bạn phải hiểu rõ toàn bộ hệ thống, còn với app được chia modules rõ ràng thì việc sửa đổi sẽ dễ hơn rất nhiều.

Screen Shot 2022-03-13 at 11 31 53

Bạn có thể đưa các đoạn code liên quan đến một interface-platform cụ thể vào các classes riêng biệt. Nhưng sẽ có rất nhiều classes mới sẽ được sinh ra. Việc kế thừa class cũng sẽ tăng nhanh chóng mặt vì việc thêm UI mới hoặc API mới sẽ đòi hỏi các classes mới.

Chúng ta có thể giải quyết vấn đề trên bằng Bridge pattern. Ta sẽ chia các classes thành 2 nhánh kế thừa:

  1. Abstraction: UI layer
  2. Implementation: OS's APIs

Screen Shot 2022-03-13 at 11 35 54

Abstraction object điểu khiển tầng UI, nó sẽ chuyển tiếp công việc xử lí cho các implementation objects. Các implementation objects khác nhau hoàn toàn có thể tráo đổi vai trò cho nhau nếu chúng cùng tuân theo interface chung cũng như cho phép UI hoạt động với Window, Linux.

Kết quả là ta có thể thay đổi UI classes mà không làm ảnh hưởng gì đến API-related classes.

Cấu trúc

Screen Shot 2022-03-13 at 11 52 12

  1. Abstraction: cung cấp high-level control. Nó sẽ chuyển tiếp việc xử lí cấp thấp (low-level) cho implementation object
  2. Implementation: định nghĩa interface cho các class triển khai. Abstraction object chỉ có thể giao tiếp với implementation object thông qua các methods được định nghĩa ở interface này.
  3. Concrete Implementations: platform-specific code.
  4. Client chỉ quan tâm tới việc tương tác với abstraction.

Tính ứng dụng

  1. Bridge Pattern giúp ta chia nhỏ Monolithic class thành các nhánh class kế thừa độc lập với nhau. Khi class càng lớn, càng dài thì sẽ càng khó để nắm bắt luồng làm việc, cũng như fix bug cho nó. Ngoài ra việc thêm tính năng mới cũng gặp nhiều khó khăn. Việc chia class thành các nhánh class kế thừa cho phép ta có thể thay đổi được class trong nhánh kế thừa này mà không làm ảnh hưởng đến các classes ở nhánh kế thừa khác.

  2. Sử dụng pattern khi bạn muốn mở rộng class theo các nhánh độc lập nhau. Từ đó original class có thể chuyển tiếp các công việc cho các class con trong cây kế thừa thay vì làm trực tiếp mọi việc.

Composite Pattern

Pattern này cho phép ta có thể kết hợp các objects lại thành một cấu trúc cây, sau đó làm việc với cấu trúc cây này như thể nó là các objects riêng lẻ.

Vấn đề

Sử dụng Composite pattern chỉ phát huy tác dụng khi core model của bạn có cấu trúc cây.
Lấy ví dụ trong thực tế, bạn có 2 loại objects: ProductsBoxes. Box có thể chứa một vài Products hoặc thậm chí các Boxes nhỏ hơn ...

Screen Shot 2022-03-13 at 12 33 26

Vấn đề ta gặp phải là khi tính tổng giá tiền. Trong thực tế ta có thể mở toàn bộ các Boxes ra rồi tính tổng tiền nhưng trong chương trình điều này không đơn giản chỉ là chạy mấy vòng lặp for là xong.

Do các classes này sẽ lồng nhau (ít hoặc nhiều tầng) nên việc tính trực tiếp như vậy là điều không thể.

Giải pháp

Composite pattern cho rằng nên sử dụng ProductsBoxes thông qua một interface chung - interface này sẽ định nghĩa method dùng cho việc tính giá tiền.

Method này sẽ hoạt động như thế nào ? Với product nó sẽ trả về giá tiền của product luôn, còn với box thì nó sẽ duyệt qua toàn bộ các items bên trong box, nếu gặp box con bên trong nó sẽ duyệt các items bên trong box con rồi tính giá tiền, cứ thế cho đến khi toàn bộ giá tiền của các components con trong box được tính thì thôi.

Method này sẽ chạy một cách đệ quy để tính giá tiền

Cách làm này có ưu điểm ở chỗ bạn không cần quan tâm đến class của object mà bạn đang tính tiền. Bạn sẽ xử lí chúng như nhau thông qua một interface chung. Khi bạn gọi method, object sẽ truyền request xuống theo độ sâu của cây.

Cấu trúc

Screen Shot 2022-03-13 at 15 24 54

  1. Component interface định nghĩa các operations chung mà các elements đơn giản và phức tạp đều có
  2. Leaf là element thấp nhất, không có sub-element. Leaf sẽ thực hiện công việc trên thực tế do chúng không thể chuyển tiếp công việc xuống cho element nào thấp hơn cả.
  3. Container có các sub element, một container không hề biết gì về class của sub element mà chỉ làm việc với sub element thông qua interface chung mà thôi. Khi nhận được request, nó sẽ chuyển tiếp xuống cho sub element hoặc các containers con. Container chỉ xử lí các kết quả trung gian và trả về kết quả cuối cùng cho client mà thôi.

Tính ứng dụng

  1. Sử dụng Composite pattern
    khi bạn cần triển khai các cấu trúc tương tự như cấu trúc cây. Composite pattern cho phép chúng ta sử dụng 2 loại components:
  • Simple leave
  • Complex container: có thể chứa leaves và các containers con khác.
  1. Pattern này cho phép client code xử lí simple và complex elements như nhau. Mọi elements đều được định nghĩa thông quan một interface chung. Bằng interface này, client không cần lo lắng về class của object mà nó làm việc cùng.

Factory Method

Là một creational design pattern. Ở superclass sẽ tạo ra các objects, nhưng ở các subclasses sẽ cho phép ta có thể thay đổi kiểu của object

Vấn đề
Giả sử bạn đang xây dựng một ứng dụng vận chuyển hoặc logistics chuyên dùng cho xe tải Truck. Ứng dụng của bạn trở nên nổi tiếng, bạn nhận được đơn hàng từ các hãng vận chuyển đường biển. Lúc này làm cách nào để đáp ứng nhu cầu của khách hàng đây, thêm class Ship song song với class Truck hiện có ? Nó có thể giải quyết vấn đề hiện tại nhưng nếu sau này phát sinh thêm một hình thái vận chuyển nữa thì sao ? Nếu vẫn theo cách tiếp cận cũ, code của bạn sẽ trở nên rất to và phức tạp.

Giải pháp
Thay vì sử dụng new trực tiếp cho constructor tương ứng với phương tiện vận chuyển, ta có thể gọi đến factory method, bên trong method này sẽ gọi đến new operator để tạo ra kết quả như là một sản phẩm.

Screen Shot 2022-01-02 at 15 48 09

Có thể nhiều người sẽ thấy rằng chỉ đơn thuần là chuyển việc gọi constructor từ một nơi sang một nơi khác. Nhưng trong thực tế ta có thể override factory method và thay đổi lại class của products được tạo ra bởi method.

Các factory method của các subclasses chỉ có thể trả về các products với kiểu khác nhau chỉ khi chúng có chung base class hoặc interface. Bản thân factory method của base class cũng phải có kiểu trả về được định nghĩa như là interface chung (được đề cập ở câu trước).

Screen Shot 2022-01-02 at 15 54 22

TruckShip class đều triển khai Transport interface, method deliver của hai class này sẽ có các cách triển khai khác nhau. Tuy vậy phía client code khi sử dụng Truck hoặc Ship đều không phân biệt được loại nào với loại nào mà client code sẽ coi chúng đều là Transport. Việc các method deliver được triển khai như thế nào hoàn toàn không quan trọng đối với client.

Screen Shot 2022-01-02 at 15 58 45

Cấu trúc

Screen Shot 2022-01-02 at 16 02 19

  1. Product: interface chung của product được tạo ra
  2. Tương tự như Truck hoặc Ship ở ví dụ trên
  3. Base class sẽ implement factory method để trả về object với kiểu Product (chức năng chính của nó KHÔNG HẲN lúc nào cũng là trả về một object mà nó còn có thể chứa các core business logic).
  4. Các subclasses sẽ override factory method.

Ta cũng có thể định nghĩa base class như là một abstract class để bắt buộc các subclasses sẽ triển khai lại factory method.

Factory method không nhất thiết phải tạo mới một instance mà có thể lấy từ cache, object pool hoặc các nguồn khác.

Ref: https://refactoring.guru/design-patterns/factory-method

Liskov Substitution Principle

Khi tiến hành extend class hãy cố gắng để bạn có thể sử dụng objects của sub-class để thay thế cho objects của parent class mà không làm ảnh hưởng tới client code.

Điều đó có nghĩa rằng sub-class nên tương thích với các behaviors hiện có của super class. Khi overriding một method, thay vì thay đổi toàn bộ logic của method đó, hãy code theo hướng mở rộng behavior cho nó.

Substitution Principle là một tập hợp các quy tắc để kiểm tra xem objects của sub-class có hoạt động được với client code hiện thời như super-class hay không.

Dưới đây là check list:

1. Param types của method trong sub-class nên match hoặc mang tính trừu tượng cao hơn so với param types của method trong superclass

VD: Class hiện thời có method feed(Cat c).

Good: nếu ở sub-class ta override method trên theo hướng sau → feed(Animal a): method này vẫn hoạt động như method của superclass do Cat là tập con của Animal, hơn thế nữa nó cũng có thể áp dụng cho nhiều use-case khác nhau như Dog, Cow, ...

Bad: nếu ở sub-class ta override method trên theo hướng → feed(BengalCat c): BengalCat là một tập con của Cat nên method này chỉ hoạt động được với một vài TH nhất định và không còn thích hợp với client code như cũ nữa.

2. Return type của method trong sub-class nên match hoặc là subtype của return type thuộc về method trong superclass

Ta có thể thấy yêu cầu về return type là ngược lại so với param type. Lấy ví dụ như sau, method của super-class buyCat(): Cat thì method của sub-class nên là buyCat(): BengalCat thay vì buyCat(): Animal.

3. Method trong sub-class không nên throw ra những Exceptions mà base method không yêu cầu phải throw ra

Nói cách khác là type của exception trong sub-class nên match hoặc là subtype của exception mà base method có thể throw ra. Trên thực tế thì try-catch block của client code thường sẽ chỉ bắt các exceptions mà base-method có thể throw ra. Việc sub-class method throw exception khác type sẽ làm cho try-catch block của client code không bắt được exception đó.

4. Sub-class không nên tăng mức độ chặt chẽ của pre-conditions. Lấy ví dụ, nếu base method param yêu cầu int nhưng sub-class lại yêu cầu positive int thì việc làm này sẽ làm tăng tính chặt chẽ của pre-condition (strengthen pre-condition). Điều này sẽ khiến cho client code cảm thấy "bối rối"

5. Sub-class không nên làm yếu post-condition. Lấy ví dụ sau khi mọi thao tác kết thúc xong, ta sẽ tiến hành đóng connection tới DB tại base method nhưng sub-class method vẫn giữ nguyên connection để tái sử dụng. Client code không hề biết về việc này nên sau khi thao tác xong, client tắt toàn bộ chương trình, dẫn tới tình trạng còn connection tới DB vẫn chưa được đóng lại.

6. Tính bất biến của super-class phải được bảo đảm. Tính bất biến có thể hiểu đơn giản như sau: một con mèo, dù là giống mèo gì đi nữa cũng vẫn sẽ có 4 chân, và kêu meow meow. Tuy nhiên tính bất biến trong thực tế lại rất dễ bị xâm phạm. Do đó khi tiến hành extends class ta chỉ nên thêm methods hoặc thêm fields mới chứ không nên chỉnh sửa lại quá nhiều những phần sẵn có của super-class.

7. Sub-class không nên thay đổi các giá trị private của super-class. Do với một vài ngôn ngữ như Python hay JS, chúng vẫn cho phép bạn có thể truy cập vào private field của super-class.

VD:

Cùng lấy một ví du về sự vi phạm quy tắc Substitution.

Screen Shot 2022-06-04 at 22 36 24

Cụ thể như sau, ta thấy sub-class throw ra một Exceptionsuper-class hoàn toàn không có, điều này sẽ làm cho client-code phụ thuộc vào class nó sử dụng. Phân tích ta sẽ thấy như sau, nếu document không phải là ReadOnlyDocument thì sẽ throw ra Exception vậy nên ở client-code (ở đây là Project) sẽ phải check xem document có phải là ReadOnlyDocument, từ đó client-code sẽ phụ thuộc vào class ReadOnlyDocument.

Sửa lại cấu trúc kế thừa, ta sẽ có như sau:

Screen Shot 2022-06-04 at 22 41 16

sub-class WritableDocument sẽ kế thừa từ super-class Document với việc viết thêm method save()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.