[Java Core] B4: Tính chất đóng gói, kế thừa và đa hình trong Java

This entry is part 4 of 8 in the series Java Core

Đóng gói

  • Đóng gói là một trong những tính chất quan trọng của lập trình hướng đối tượng
  • Đóng gói giúp che dấu thông tin bên trong của một đối tượng, chỉ cho phép các đối tượng khác tương tác thông qua các phương thức được cung cấp
  • Đóng gói giúp giảm sự phức tạp của chương trình, giúp dễ dàng bảo trì và mở rộng chương trình
Encapsulation
  • Ví dụ, trong một chiếc xe ô tô, ta có rất nhiều cấu phận và thông số. Nó có động cơ, vô lăng, bình xăng, đánh lửa, …
  • Nhưng ta thường không tương tác trực tiếp được vào chúng, mà thông qua các phương thức như bấm ga, bấm còi, đạp phanh, …
  • Khi bấm ga, có thể cả một quy trình rất dài đang xảy ra, như động cơ chạy, bình xăng bơm xăng, đánh lửa đốt xăng, …
  • Nhưng ta không cần quan tâm đến những thứ đó, ta chỉ cần bấm ga, và xe sẽ chạy
  • Ngoài ra, đóng gói giúp ta đảm bảo việc dữ liệu sẽ được giấu khỏi các đối tượng khác, và chỉ có các phương thức được cung cấp mới có thể tương tác với dữ liệu đó. Giúp đảm bảo tính toàn vẹn của dữ liệu.

Ví dụ:

  • getter, setter là một ví dụ về đóng gói. Nhờ getter, setter, ta có thể đảm bảo dữ liệu được cập nhật từ đối tượng khác sẽ luôn theo ý của mình, hay dữ liệu được lấy ra từ đối tượng khác sẽ luôn đúng theo ý của mình
  • Ví dụ trong các thư viện của Java, ta có thể thấy rất nhiều ví dụ về đóng gói. Ví dụ như trong class Scanner, ta có thể thấy rằng các biến được khai báo là private, và ta không thể truy cập trực tiếp vào chúng. Nhưng ta có thể sử dụng các phương thức như nextInt(), nextDouble(), nextLine(), … để lấy dữ liệu từ bàn phím. (Có thể ctrl + click vào các phương thức đó để xem code, sẽ thấy Scanner sử dụng các biến private để lấy dữ liệu từ bàn phím)
image 26 - quochung.cyou PTIT
  • Đóng gói xuất hiện ở mọi nơi: Từ cả cái máy tính của bạn chỉ cần 1 nút bật, một game với cả trăm nghìn class chỉ cần 1 lần nhấn Play sẽ bắt đầu chạy, các thư viện Java với vô vàn chức năng đã được đóng gói và cho phép ta sử dụng qua những hàm rất đơn giản.
  • Trong lập trình phần mềm, yêu cầu của một chương trình thay đổi liên tục đáp ứng nhu cầu của khách hàng. Việc đóng gói và phân tách công việc của những bộ phận khác nhau là cực kì quan trọng, khi đó, ta sẽ cố gắng giới hạn những thay đổi chỉ bên trong 1 bộ phận nhỏ, độc lập nhất có thể và ít ảnh hưởng tới nhiều chỗ khác.
image 28 - quochung.cyou PTIT

Kế thừa

  • Kế thừa là một trong những tính chất quan trọng của lập trình hướng đối tượng
  • Kế thừa giúp ta có thể sử dụng lại các đặc tính của một đối tượng khác, giúp giảm sự lặp lại của code, giúp dễ dàng bảo trì và mở rộng chương trình
  • Kế thừa giúp ta có thể tạo ra các đối tượng mới từ các đối tượng đã có, và có thể thêm các đặc tính mới vào các đối tượng mới đó
Inheritance
image 27 - quochung.cyou PTIT
  • Ta nhận thấy kế thừa, hay sơ đồ cây xuất hiện ở mọi nơi trong cuộc sống. Như một chiếc ô tô có thể gọi là xe bốn bánh, xe bốn bánh có thể coi là phương tiện giao thông, …. Con người có thể coi là động vật. trong sinh học ta có các giống loài, trong vật lý có nhiều loại sóng khác nhau cùng là sóng,có chung tính chất là sóng, …
image 29 - quochung.cyou PTIT

Ví dụ:

  • Ví dụ ta có nhiều class cần tạo như Chó, Mèo, Gà, Vịt, … Ta nhận thấy chúng đều có chung 1 đặc điểm là chúng đều là động vật, và có chung 1 số đặc điểm như là có thể ăn, có thể ngủ, có thể chạy, có thể bơi, … Ta có thể tạo ra một class cha là Động vật, và các class trên sẽ kế thừa từ class Động vật. Như vậy, ta có thể sử dụng lại các đặc điểm của class Động vật cho các class con, và có thể thêm các đặc điểm mới vào các class con đó, ví dụ Chó thì có thêm đặc điểm là sủa, Mèo thì có thêm đặc điểm là kêu meo meo, …
  • Các phương thức, thuộc tính của class cha sẽ được các class con kế thừa, và có thể sử dụng lại. Ngoài ra, các class con có thể thêm các phương thức, thuộc tính mới vào các class con đó. Điều này giúp giảm bớt các code dư thừa, giúp dễ dàng bảo trì và mở rộng chương trình
  • Có thể ở các class nhỏ hiện tại các bạn chưa thấy kế thừa hữu ích. Tuy nhiên khi bài toán ngày càng lớn lên, đặc biệt thiên về domain chuyên ngành, 1 class sẽ có rất nhiều thuộc tính và phương thức. Ví dụ: một cái bánh bình thường ở tạp hoá, có thể có lên tới 10-15 thuộc tính như màu sắc, hình dạng, giá, hạn sử dụng, ngày sản xuất, thành phần sản phẩm, các chứng chỉ an toàn thực phẩm, mã barcode, hay có các biến như “có thể cho động vật ăn”, … sau đó lại có nhiều loại bánh khác nhau nữa
image 17 - quochung.cyou PTIT

Class Object

Class Object
  • Trong Java, mọi class đều kế thừa từ class Object
  • Class Object là class cha của tất cả các class khác
  • Class Object có các phương thức như equals()toString()hashCode()clone()finalize()wait()notify()notifyAll(), …
  • Các phương thức này được các class khác kế thừa, và có thể sử dụng lại
  • Có nhiều phương thức các bạn chưa cần đào sâu. Tuy nhiên tạm thời có thể để ý nó có các phương thức quan trọng như equals, toString, hashCode. Đây là các phương thức thường được dùng.

Đa hình

image 30 - quochung.cyou PTIT
  • Đa hình là một trong những tính chất quan trọng của lập trình hướng đối tượng
  • Đa hình giúp ta có thể sử dụng các đối tượng khác nhau, nhưng có cùng một kiểu dữ liệu, giúp giảm sự lặp lại của code, giúp dễ dàng bảo trì và mở rộng chương trình
Polymorphism

Override

  • Override là một kĩ thuật trong đa hình, giúp ta có thể thay đổi cách thức hoạt động của một phương thức đã có sẵn trong class cha
  • Override giúp ta có thể sử dụng các đối tượng khác nhau, nhưng có cùng một class cha, và khi gọi các phương thức, ta sẽ thấy mỗi đối tượng sẽ thực hiện theo cách của nó, chứ không phải cách của đối tượng khác

Cú pháp:

publicclassClassCha {
    @OverridepublicvoidmethodName() {
        // code
    }
}

classClassConextendsClassCon {
    @OverridepublicvoidmethodName() {
        // code
    }
}

Overload

  • Overload là một kĩ thuật trong đa hình, giúp ta có thể tạo ra nhiều phương thức cùng tên, nhưng khác nhau về tham số truyền vào
  • Overload giúp ta có thể sử dụng các phương thức khác nhau, nhưng có cùng một tên, và khi gọi các phương thức, ta sẽ thấy mỗi phương thức sẽ thực hiện theo cách của nó, chứ không phải cách của phương thức khác

Cú pháp:

publicclassClassCha {
    publicvoidmethodName(int a) {
        // code
    }

    publicvoidmethodName(int a, int b) {
        // code
    }
}

  • Dễ thấy overload trong các phương thức của class Math, như Math.max()Math.min()Math.abs()Math.pow()Math.sqrt()Math.round()Math.floor()Math.ceil()Math.random(), … Chúng đều có cùng một tên là Math, nhưng khác nhau về tham số truyền vào. Ví dụ nếu truyền vào số nguyên, thì xử lý khác, truyền số thực thì xử lý khác, …

Phân biệt Overload và Override

OverloadOverride
Cùng tên, khác tham sốCùng tên, cùng tham số
Cùng classKhác class
Overload giúp ta có thể tạo ra nhiều phương thức cùng tên, nhưng khác nhau về tham số truyền vàoOverride là một kĩ thuật trong đa hình, giúp ta có thể thay đổi cách thức hoạt động của một phương thức đã có sẵn trong class cha
Overload chạy trong compile timeOverride chạy trong run time

Đa hình compile time và runtime

  • Đa hình compile time là đa hình mà chúng ta có thể thấy được ngay trong quá trình code, ví dụ như overload. Đa hình compile time sẽ được thực hiện trong compile time, tức là khi chúng ta build chương trình, chúng ta sẽ thấy các phương thức được overload sẽ được thực hiện theo cách của nó, chứ không phải cách của phương thức khác
  • Đa hình runtime là đa hình mà chúng ta không thể thấy được ngay trong quá trình code, ví dụ như override. Đa hình runtime sẽ được thực hiện trong runtime, tức là khi chúng ta chạy chương trình, chúng ta sẽ thấy các phương thức được override sẽ được thực hiện theo cách của nó, chứ không phải cách của phương thức khác

Ví dụ: Khi viết code lúc compile ta đã thấy ngay phương thức nào được gọi, nhưng khi chạy chương trình, ta mới thấy phương thức nào được thực hiện của override

Ví dụ:

  • Ta có các class Chó, Mèo, Bò. Chúng đều kế thừa từ class Động vật. Ta có thể tạo ra một mảng các đối tượng Động vật, và có thể thêm vào đó các đối tượng Chó, Mèo, Bò. Như vậy, ta có thể sử dụng các đối tượng khác nhau, nhưng có cùng một kiểu dữ liệu, là Động vật.
  • Chúng có thể có chung 1 hàm là keu(), nhưng khi gọi hàm keu() trên mảng các đối tượng Động vật, ta sẽ thấy mỗi đối tượng sẽ keu() theo cách của nó, chứ không phải cách của đối tượng khác. Như vậy, ta có thể sử dụng các đối tượng khác nhau, nhưng có cùng một kiểu dữ liệu, và khi gọi các phương thức, ta sẽ thấy mỗi đối tượng sẽ thực hiện theo cách của nó, chứ không phải cách của đối tượng khác.
publicclassDongVat {
    publicvoidkeu() {
        System.out.println("Keu cua dong vat");
    }
}

publicclassChoextendsDongVat {
    @Overridepublicvoidkeu() {
        System.out.println("Gau gau");
    }
}

publicclassMeoextendsDongVat {
    @Overridepublicvoidkeu() {
        System.out.println("Meo meo");
    }
}

publicclassBoextendsDongVat {
    @Overridepublicvoidkeu() {
        System.out.println("Um bo");
    }
}

Bài tập kế thừa và đa hình

  • Tạo ra một class ConNguoi, có các thuộc tính là tên, tuổi, giới tính, …
  • Có các class con là SinhVien, NhanVien, HocSinhCap3 kế thừa từ class ConNguoi
  • Các class con có thể có thêm các thuộc tính riêng của nó, ví dụ SinhVien có thuộc tính là mã sinh viên, NhanVien có thuộc tính là mã nhân viên, …
  • Các class con có thể có thêm các phương thức riêng của nó, ví dụ SinhVien có phương thức là học, NhanVien có phương thức là làm việc, …
  • Có 1 phương thức chung của 3 class con là lamViecBuoiSang(), nhưng mỗi class con sẽ thực hiện theo cách của nó, chứ không phải cách của class con khác. Ví dụ SinhVien sẽ đi học đại học, NhanVien sẽ đi làm, HocSinhCap3 sẽ đi học ở trường cấp 3

[Pessimistic locking] Xử lí dữ liệu bất đồng bộ trong thực tế

image 54 - quochung.cyou PTIT

Pessimistic locking là gì?

Đọc vấn đề xảy ra về dữ liệu đồng thời tại : http://quochung.cyou/optimistic-locking-xu-li-du-lieu-bat-dong-bo-trong-thuc-te/

Pessimistic locking (Khoá bi quan) là một chiến thuật xử lí có thể tổng quát rằng, giả dụ, bạn có một tập dữ liệu đang bị truy cập đồng thời, người đầu tiên truy cập sẽ khoá tập dữ liệu này lại, độc quyền sửa đổi nó, cho đến khi phiên đó hoàn thành. Điều này đảm bảo dữ liệu sẽ chuẩn xác hơn khoá lạc quan, nhưng cũng có thể tạo ra Deadlock

Ví dụ bài toán

  • Bạn đang chơi một con game sinh tồn, có một chiếc rương chứa đồ chung mà hai người chơi đều có thể mở, nhưng nếu cả hai cùng mở, người này lấy đồ, người kia lại cho đồ vào, đôi khi dữ liệu sẽ bị loạn
  • Khoá bi quan sẽ xử lí như sau
  • Khi người chơi A mở rương, khoá rương lại. Lúc này người chơi khác ấn vào rương sẽ bị thông báo như dạng “rương đang được mở bởi người khác”, và không thể truy cập cho đến khi người chơi A đóng rương.
image 56 - quochung.cyou PTIT
image 57 - quochung.cyou PTIT

Deadlock

  • Deadlock xảy ra khi hai phiên không thể tiến triển được nữa, vì cùng phụ thuộc vào nhau chờ phía bên kia mở khoá, có thể xem hình dưới đây
image 58 - quochung.cyou PTIT
  • Ta có một database post chứa các bài viết. Trong đó có 1 bài viết id 1 và title là “Transaction”
  • Ngoài ra ta có 1 post chứa post_detail, là nội dung bài viết, số lượng like comment, …
  • Alice đầu tiên truy cập vào bảng post_detail, cập nhật số lượng like comment. Lúc này bảng post_detail thay đổi “người thay đổi cuối” thành Alice. Alice lock bảng post_detail
  • Lúc này Bob cũng truy cập vào bảng post, đổi tên nó thành thứ khác. Bob lock bảng post
  • Do bảng post và postdetail liên kết với nhau, lúc này ở phiên của Bob sẽ gọi về post_detail của post id 1 để thay đổi “người thay đổi” thành Bob. Tuy nhiên, nó đang bị khoá bởi Alice, vì vậy phải chờ Alice xong đã
  • Phía Alice lúc này cũng cập nhật lên bảng post, nhưng nó lại bị khoá bởi Bob, nên cũng phải đợi Bob xong đã
  • => Điều này trở thành vòng tuần hoàn vô hạn, cho đến khi hệ quản trị cơ sở dữ liệu có cơ chế phát hiện ra, và huỷ cả 2 phiên.

Deadlock ở mọi nơi: hệ điều hành, trong code đa luồng, ..

image 59 - quochung.cyou PTIT
  • Deadlock không chỉ xảy ra trong database, nó có thể xảy ra trong mọi hệ thống. Trong hệ điều hành, trong …., chỉ cần nếu chúng có thể truy cập từ nhiều nguồn cùng lúc
  • Ví dụ, đa luồng trong Java cũng có thể tạo deadlock khi 2 luồng cùng đợi nhau chứ không ai chạy trước

Cách xử lí

  • Khi Alice thực hiện khoá post, ta có thể khoá luôn cả post_Detail và các bảng liên quan
  • Hệ thống của ta cần có cơ chế phát hiện deadlock, ví dụ lock chờ đợi quá lâu, …

Đọc thêm:

High Cohesion (Tương đồng cao) và Loosely Coupled (Liên kết lỏng)

image 36 - quochung.cyou PTIT

Mở đầu

Có thể bạn đã được nghe về các thuật ngữ này từ các câu hỏi trong các buổi phỏng vấn, trong một bài đăng medium nào đó, trong một cuộc thảo luận của anh em trong giang hồ. Vậy chính xác các term như high cohesion, low cohesion, loosely coupled, highly coupled có nghĩa là gì?

Cohesion (Tương đồng – Gắn kết)

image 34 - quochung.cyou PTIT

In computer programmingcohesion refers to the degree to which the elements inside a module belong together.[1] In one sense, it is a measure of the strength of relationship between the methods and data of a class and some unifying purpose or concept served by that class. In another sense, it is a measure of the strength of relationship between the class’s methods and data themselves.

Cohesion is an ordinal type of measurement and is usually described as “high cohesion” or “low cohesion”. Modules with high cohesion tend to be preferable, because high cohesion is associated with several desirable traits of software including robustness, reliability, reusability, and understandability. In contrast, low cohesion is associated with undesirable traits such as being difficult to maintain, test, reuse, or even understand.

Cohesion is often contrasted with coupling. High cohesion often correlates with loose coupling, and vice versa.[2] The software metrics of coupling and cohesion were invented by Larry Constantine in the late 1960s as part of Structured Design, based on characteristics of “good” programming practices that reduced maintenance and modification costs. Structured Design, cohesion and coupling were published in the article Stevens, Myers & Constantine (1974)[3] and the book Yourdon & Constantine (1979);[1] the latter two subsequently became standard terms in software engineering

Wikipedia

image 35 - quochung.cyou PTIT

Bản dịch

  • Trong khoa học máy tính, cohesion (tính tương đồng) thường được nhắc tới như một góc độ đánh giá nào đó, về việc các phần tử trong 1 module “tương đồng” với nhau.
  • Thông thường có thể nhắc tới việc sự liên kết bền chặt giữa các hàm (function) và các biến (property) trong 1 class có sự liên kết với nhau
  • Thông thường sẽ có 2 định nghĩa chính là “high cohesion” (tương đồng cao) và “low cohesion” (tương đồng thấp).
  • High cohesion thường đi cùng với việc code chặt chẽ hơn, dễ tái sử dụng, dễ bảo trì và dễ hiểu.

Vậy có thể hiểu là, high cohesion thường được ám chỉ các hàm và các biến trong 1 class đều cùng phục vụ mục đích, hoặc được sử dụng cho một ý nghĩa gì đó có tính tương đồng cao với nhau.

  • Trong cuốn Clean Code của Uncle Bobs có viết:
  • “Một class chỉ nên có ít biến. Các hàm trong class đó nên tác động lên một hoặc nhiều các biến trên. Nếu các hàm trong 1 class càng dùng nhiều các biến có sẵn, thì class có tính tương đồng càng cao”
  • => Tức là một class được đánh giá là tương đồng cao nên chỉ làm các tác vụ bên trong class đó, phục vụ chung 1 mục đích.

Ví dụ:

  • Bạn có một nhân vật trong 1 con game của mình. Bạn có các code logic cho việc nhân vật di chuyển từ các nút bấm trên bàn phím, và một số code logic để hiển thị việc nhân vật di chuyển trên trục toạ độ trên màn hình (render hình ảnh)
  • Nếu toàn bộ code đều nằm trong 1 class, code của bạn có 1 số hàm xử lí toán học cho nhân vật di chuyển, lại có 1 số hàm, biến để render hiển thị lên. Rõ ràng chúng có tính tương đồng yếu. Một cách đơn giản để nhận biết, bạn có thể nhìn các tên biến, tên hàm xem chúng có liên quan đến tên class không
  • Ví dụ: class tên là “InvoiceManager” (Quản lý hoá đơn), nếu có 1 hàm nào đó tên là “InHoaDon” thì cũng k ổn lắm, nó nên nằm trong “Printer”, hay có 1 hàm nào đó là “GuiEmailHoaDon” (thì hàm đó cũng nên làm trong class nào đó tên là EmailManager, …)

Coupled/Coupling (Tính liên kết)

  • Thoạt nghe thì nó có vẻ là 1 thuật ngữ trái ngược với cohesion? Vậy High Cohesion (tương đồng cao) thì sẽ có Tightly Coupling (Liên kết chặt) ?
  • Ở trường hợp này thì không. Cohesion thường dùng để xem xét giữa các phần tử (hàm, biến) trong 1 module (class). Trong khi Couple thường dùng để đánh giá tính liên kết giữa nhiều module
image 37 - quochung.cyou PTIT
  • Liên kết lỏng cho rằng, giữa các module với nhau, chúng không nên biết quá nhiều về nhau (liên kết quá chặt chẽ), mà chỉ nên giao tiếp với nhau qua 1 số phương thức mang tính “khái quát” nhất định một cách lỏng lẻo (loosely coupled)
image 39 - quochung.cyou PTIT
  • Ví dụ: Chiếc mũ đang “liên kết lỏng lẻo” với người đàn ông. Bạn dễ dàng thêm hoặc bỏ cái mũ ra mà không phải thay đổi gì người đàn ông cả. Khi code bạn làm được như vậy, thì nó là liên kết lỏng lẻo (loosely coupled)
image 40 - quochung.cyou PTIT

Ví dụ:

  • Bạn đang có một class xử lí việc bán lẻ bánh ở cửa hàng của mình tên là “CuaHang”. Cửa hàng sẽ lấy bánh từ class “XuongBanh”. Ban đầu class của bạn chỉ cần cứ lấy bánh từ Xưởng rồi bán thôi, việc rất đơn giản
image 41 - quochung.cyou PTIT
  • Nhưng một ngày đẹp trời, bạn nhận ra khách có vẻ thích những chiếc bánh có một chút muối, rồi thì bánh có thêm chút trang trí. Dĩ nhiên việc thêm chút thứ gia vị này, bạn cần thêm một chút tiền vào giá hàng, bạn sửa code thành như sau:
image 42 - quochung.cyou PTIT
  • Bạn có thể thấy, điều này đáp ứng nhu cầu của bạn. Tuy nhiên bạn thấy rõ ràng lúc này class Banh và CuaHang đang gắn rất chặt với nhau. CuaHang can thiệp quá nhiều vào class Banh, và có những phương thức không phù hợp lắm. (CuaHang chỉ nên là bán hàng thôi, chỉ nên có giá tiền). Rõ ràng bạn có thể làm chúng liên kết lỏng hơn như sau:
image 43 - quochung.cyou PTIT
  • Rõ ràng, lúc này class CuaHang chỉ cần tập trung lấy bánh và sau đó tính ra tổng tiền. Không cần quan tâm giá từng loại bánh từ xưởng nữa, vì việc tính tiền giá trị 1 đơn hàng sẽ được thực hiện bởi 1 class khác. CuaHang chỉ cần biết tôi sẽ có được tổng tiền từ hàm này, chứ không cần biết chính xác các class kia sẽ cụ thể làm gì, tính cái gì nữa (liên kết lỏng)
  • Mở rộng hơn nữa, ví dụ một ngày bạn muốn bán kẹo thì sao? Bán gấu bông? … Nhu cầu thị trường luôn thay đổi, bạn luôn muốn giàu hơn và mở rộng hơn nữa, lúc này bạn lại nhận ra, class CuaHang đang phụ thuộc quá nhiều vào xưởng bánh, mà thực chất ta chỉ cần lấy ra danh sách sản phẩm từ 1 xưởng nào đó thôi, ta có thể sửa thành như sau:
image 44 - quochung.cyou PTIT
  • Ta tạo 1 interface VatPham luôn có 1 phương thức tinhTien(), sau này, ta có thể làm bánh, kẹo, trái cây, .. chỉ cần chúng implement interface VatPham, ta luôn đảm bảo ta có thể lấy giá tiền từ chúng, còn implemention thực tế chúng tính tiền thế nào là việc của class con đó.
  • Tương tự, ta có 1 interface XuongSanXuat luôn trả về các danh sách vật phẩm, chúng có thể là xưởng bánh trả về bánh, xưởng kẹo trả về kẹo
  • Cửa hàng bây giờ khi khởi tạo có thể truyền vào xưởng mà chúng sẽ nhận đồ để bán
  • Cuối cùng, chuỗi cửa hàng của ta với nhiều cửa hàng, có thể có cửa hàng bánh, cửa hàng kẹo, còn việc chúng tính tiền thế nào làm gì cũng không phải việc của class lớn nữa. Dự án đã lỏng lẻo hơn nhiều ban đầu, và dễ mở rộng hơn.

Tổng kết

  • Với các dự án lập trình, thông thường chúng ta nên nhắm tới việc đạt được cùng lúc 2 yếu tố: High Cohesion (tương đồng cao trong 1 class/module) và Loosely Coupled (liên kết lỏng giữa nhiều module)
  • Việc đạt được 2 yếu tố trên giúp code dễ mở rộng, dễ bảo trì, ta dễ dàng tìm được nguyên căn nơi tạo lỗi vì chúng không share chung các code nhỏ với nhau, nên lỗi chỉ nên từ 1 nguồn
  • Đây là 1 kĩ năng thiết yếu với lập trình viên cho các dự án ngày càng cần mở rộng
  • High Cohesion cũng khá tương đồng với SRP trong SOLID, bạn có thể tìm hiểu thêm về SOLID tại đây http://quochung.cyou/tim-hieu-solid-5-nguyen-tac-thiet-ke-huong-doi-tuong-bang-java/

[Optimistic locking] Xử lí dữ liệu bất đồng bộ trong thực tế

image 30 - quochung.cyou PTIT

Optimistic locking là gì?

Đây là một kĩ thuật xử lí vấn đề khi người dùng cùng làm việc trên một tập dữ liệu, nhưng sẽ không ảnh hưởng đến nhau. Nói cách khác, không có một khoá hay vấn đề gì ngăn cản người dùng cả, chúng ta chỉ kiểm tra lazy xem đang có một phiên nào khác đang sửa tập dữ liệu này không. Nếu có, ta sẽ rollback lại dữ liệu

Bài toán thực tế

  • Giả sử, bạn đang làm việc trên một bài toán khá đơn giản: một cửa hàng bán đồ điện tử. Nhiệm vụ của bạn là một giao diện admin để có thể lấy toàn bộ sản phẩm, thêm mới, cập nhật vài sản phẩm nào đó, xoá vài sản phẩm đi. Một bài toán CRUD đơn giản.
optimisticlocking - quochung.cyou PTIT
  • Lúc này, ta có 2 nhân viên đang trực ca và sử dụng hệ thống – Alice và Bob. Một ngày đẹp trời, Alice mới nhập kho vài chiếc màn máy tính phiên bản mới “Acer 2023”, cô muốn sửa tên “Acer 2022” trong sản phẩm cửa hàng thành “Acer 2023”. Đúng lúc này, Bob cũng đang muốn sửa tên của sản phẩm “Acer 2022” một cách fancy hơn, như là “Acer 2022 144hz”.

Quá trình xảy ra như sau:

  • Alice mở thông tin của món hàng, mở lên giao diện sửa tên. Bỗng nhiên, máy pha cà phê của cô báo hoàn thành, cô lập tức chạy đi lấy cà phê
  • Ở phía Bob, anh cũng đang mở thông tin món hàng “Acer 2022”, sửa tên thành “Acer 2022 144hz”, sau đó đi chơi
  • Alice quay trở lại, cô cũng nhập tên “Acer 2023”, sau đó ấn nút save
  • Bob đi chơi về và: ủa? sao lại thành tên này rồi?. Anh hỏi Alice thì cô cũng một mực khẳng định là tên hiển thị trong giao diện sửa của cô lúc sửa là “Acer 2022”, chứ chưa phải là “Acer 2022 144hz”

Vậy ai là người sai ở đây?

  • Là Alice, người ấn save lần cuối
  • Hay là Bob, người ấn đầu tiên?

Để xử lí vấn đề này, Optimistic locking đưa ra cách giải quyết là: các user cần được cập nhật rằng tập dữ liệu đã thay đổi

Một vài ví dụ có thể bạn từng thấy trong các ứng dụng về Optimistic locking

  • Bạn đang ở màn hình chọn voucher của shopee, sau khi chọn xong và chuẩn bị thanh toán. Lúc ấn thanh toán, bạn được báo “một vài voucher đã hết/thay đổi, vui lòng xem lại”
  • Bạn đang xem giá xe ở Grab, thì có thông báo yêu cầu reload lại do giá đã thay đổi
  • Bạn xem một post ở Facebook, lúc viết comment thì có thông báo “bài viết có thể không tồn tại nữa”
  • Khi bạn thực hiện commit

Các phase của Optimistic locking

  • Lưu lại timestamp, thời gian cuối khi mà một phiên bắt đầu
  • Bắt đầu sửa dữ liệu
  • Validate kiểm tra lại, kiểm tra xem thời gian lần cuối lưu ở phía client có giống với thời gian ở phía database không
  • Nếu không có conflict xảy ra, cập nhật mọi dữ liệu. Còn không thì resolve nó, có thể là huỷ phiên hiện tại, hoặc cho phép merge, ….
image 31 - quochung.cyou PTIT

Áp vào bài toán của chúng ta, ta có flow như sau

  • Alice thực hiện sửa ở phía local của Alice, ở database lúc này lưu “số phiên bản” của món đồ này là 1
  • Bob thực hiện sửa, sau đó lưu lại. Database cập nhật phiên bản lúc này đã là “2”
  • Alice ấn nút lưu, phát hiện phiên bản sai lệch, đưa ra cách xử lí nào đó cho người dùng

Tổng kết

Optimistic locking ngăn các vấn đề conflict dữ liệu xảy ra bằng cách kiểm tra conflict, sau đó tạo ra các cách resolve nó (merge, bỏ phiên, …)

  • Nó được gọi là Optimistic locking (khoá lạc quan)…. vì chúng ta lạc quan là khả năng conflict khá ít xảy ra
  • Phù hợp với nhiều món hàng, record và ít nguwofi dùng
  • Cho phép 1 cách nhiều người dùng làm trên 1 dữ liệu
  • Tốn ít công sức hơn, không cần khoá, …
  • Nhưng nếu conflict xảy ra, thường user sẽ phải làm lại phiên hoàn toàn …

Tham khảo:

Đọc thêm:

[Web-Buổi 1] Tìm hiểu về cách hoạt động của Web, HTML cơ bản

Mục lục:

  • Cách thức hoạt động của một trang web
  • Cấu trúc của HTML
  • Block, inline elements. Qua phân biệt div, span.
  • Các thẻ liên quan đến table trong HTML
  • Các thẻ: image, video, links, audio
  • Section elements: div, span, header, footer, nav, main, section, article
  • HTML text fundamentals: h1 -> h6, p, ul, li
  • Các thẻ liên quan đến form: form, input, button, label, textarea
  • Có thể tìm hiểu các thẻ khác ngoài các thẻ trên các thẻ trên

I. Cách thức hoạt động của một trang web

Khi máy tính nhà của mình kết nối đến một trang web trên mạng, ta có thể gọi chúng là các clients (máy khách) và servers (máy chủ).

Ảnh minh hoạ: ảnh minh hoạ 1

Client máy khách sẽ gửi các Requests (yêu cầu) về máy chủ, và máy chủ sẽ trả về Response (Phản hồi) tương ứng với yêu cầu của người dùng.

VD:

  • Người dùng gửi yêu cầu tìm trang bạn bè, server trả về trang banbe.html
  • Người dùng gửi yêu cầu nhắn tin, server trả về dữ liệu tin nhắn

Ở thời kì đầu của web, đa phần các web đều là web tĩnh. Tức là các trang web chỉ có thể hiển thị các nội dung cố định, không thể thay đổi được. Ví dụ như các trang web tin tức, các trang web thông tin, các trang web giới thiệu sản phẩm, …

Tuy nhiên, với sự phát triển của công nghệ, các trang web ngày nay có thể thay đổi được nội dung, có thể tương tác với người dùng. Ví dụ như các trang web mạng xã hội, các trang web thương mại điện tử, các trang web chơi game, … (Được gọi là web động). Các trang web động sẽ có dữ liệu hiển thị tuỳ theo người dùng khác nhau, và có thể thay đổi được dữ liệu hiển thị tuỳ theo người dùng. Ví dụ như trang web mạng xã hội, khi bạn đăng nhập vào trang web, bạn sẽ thấy các bài viết của bạn bè, và bạn có thể đăng bài viết của mình lên trang web.

ảnh minh hoạ 2

Vậy nhìn chung thì Web là một dữ liệu sẽ được server trả về khi người dùng yêu cầu. Tuy nhiên nếu đào sâu ra thì đây là một quá trình rất dài, tưởng tượng máy bạn là nhà bạn, và web bạn truy cập là một cửa hàng trên phố, lúc này để truy cập web, bạn cần đi qua một tuyến đường để đến cửa hàng đó.

Quá trình xảy ra khi bạn kết nối một trang web:

  • Đầu tiên tưởng tượng, để đến một cửa hàng (web), ta cần địa chỉ của nó. Các máy chủ hay máy người dùng khi kết nối đến hệ thống internet toàn cầu đều được cấp một địa chỉ, thường là Ipv4 (ví dụ 98.139.180.149)
  • Tuy nhiên, số lượng ipv4 là có hạn. Vì vậy thông thường các máy tính sẽ nằm trong một mạng cục bộ, và chỉ có một ipv4 bên ngoài quyết định chúng. VD: Toàn bộ máy tính của 1 toà nhà sẽ đều kết nối vào một modem mạng có Ipv4 là (98.139.180.149). Đây sẽ là địa chỉ để bên ngoài có thể kết nối tới. Tuy nhiên khi có yêu cầu gửi tới, ta có thể cài đặt nó sẽ được đi vào máy tính nào trong toà nhà đó, các máy tính lúc này sẽ có một ipv4 cục bộ kiểu: 192.168.0.1, 192.168.0.2, ….. Tưởng tượng: Các cửa hàng nằm trong phố Nguyễn Trãi, ta đi đến Nguyễn Trãi và nó sẽ là số 100 Nguyễn Trãi, số 101 Nguyễn Trãi, …
ảnh minh hoạ 3
  • Như vậy ta đã biết là bằng cách trên. Một server có thể truy cập bằng một địa chỉ Ipv4. Kiểu ta có thể truy cập bằng http://98.139.180.149. Tuy nhiên người dùng không thể nhớ hết số IP này, vì vậy ta cần một hệ thống định dang (tên miền – domain) để biến ip số này thành các tên miền dễ nhớ. VD: http://google.com, http://facebook.com, http://youtube.com, …
  • Để có thể biết được địa chỉ ip của một tên miền, ta cần một hệ thống DNS (Domain Name System). Hệ thống này sẽ lưu trữ các tên miền và địa chỉ ip tương ứng của nó. VD: google.com -> 20.002.003.149
  • Các hệ thống DNS như một cuốn từ điển và cho biến tên miền nào chỉ đến ip nào. Thông thường khi kết nối, đầu tiên máy tính sẽ tìm lại vào cache của chính máy mình để xem tên miền đã được lưu chưa, nếu chưa có thì máy tính sẽ tìm đến các dns server của ISP (nhà mạng, ví dụ Viettel, VNPT). Nếu vẫn chưa tìm thấy, máy tính sẽ tìm đến các DNS Server khác như của Google (8.8.8.8), Cloudflare (1.1.1.1), …
  • Khi người dùng gõ tên miền vào trình duyệt, trình duyệt sẽ gửi yêu cầu tới DNS để lấy địa chỉ ip của tên miền đó. Sau đó trình duyệt sẽ gửi yêu cầu tới địa chỉ ip đó, và server sẽ trả về dữ liệu tương ứng với yêu cầu của người dùng.
ảnh minh hoạ 4

II. Cấu trúc của HTML

ảnh minh hoạ 5
ảnh minh hoạ 6
  • HTML là một ngôn ngữ đánh dấu siêu văn bản (HyperText Markup Language). Nó được sử dụng để xây dựng các trang web. HTML được sử dụng để đánh dấu các phần tử trong một trang web. Các phần tử này được đánh dấu bằng các thẻ HTML. Mỗi thẻ HTML bắt đầu bằng một ký tự đặc biệt và kết thúc bằng một ký tự đặc biệt. Nội dung giữa các thẻ HTML là nội dung của phần tử đó. Ví dụ: <p> This is a paragraph </p>
  • Các thẻ HTML có thể được phân thành hai loại: thẻ đóng và thẻ không đóng. Thẻ đóng có một thẻ đóng tương ứng, trong khi thẻ không đóng không có thẻ đóng tương ứng. Ví dụ: <p> This is a paragraph </p> là một thẻ đóng, trong khi <br> là một thẻ không đóng.

III. Thẻ khối và thẻ nội tuyến

Các thẻ HTML có thể được nhóm thành các thẻ khối và các thẻ nội tuyến. Các thẻ khối bắt đầu trên một dòng mới và chiếm toàn bộ chiều rộng của trình duyệt, trong khi các thẻ nội tuyến không bắt đầu trên một dòng mới và chỉ chiếm một phần của chiều rộng của trình duyệt. Ví dụ: <p> là một thẻ khối, trong khi <b> là một thẻ nội tuyến.

Có 2 kiểu giá trị hiển thị chính : block và inline.

Block-level elements (thẻ khối) :

Một block-elements luôn bắt đầu trên một dòng mới và Browser tự động thêm khoảng trắng (margin) và trước và sau phần tử đó. Nó luôn chiếm toàn bộ chiều rộng có sẵn. 2 block-elements thường dùng là : <p> và <div>.

Inline elements (thẻ nội tuyến) :

Một Inline elements không bắt đầu trên một dòng mới. Nó chỉ chiếm chiều rộng cần thiết. Một phần tử inline không thể chưa phần tử block.

  • <div> là một thẻ khối, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <div> This is a div </div>
  • <span> là một thẻ nội tuyến, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <span> This is a span </span>

IV. Các thẻ liên quan đến table trong HTML

  • <table> là một thẻ khối, nó được sử dụng để tạo một bảng. Nó có thể chứa một hoặc nhiều thẻ <tr>. Ví dụ: <table> This is a table </table>
  • <tr> là một thẻ khối, nó được sử dụng để tạo một hàng trong bảng. Nó có thể chứa một hoặc nhiều thẻ <td>. Ví dụ: <tr> This is a table row </tr>
  • <td> là một thẻ nội tuyến, nó được sử dụng để tạo một ô trong bảng. Nó có thể chứa một hoặc nhiều thẻ <p>. Ví dụ: <td> This is a table cell </td>
  • <th> là một thẻ nội tuyến, nó được sử dụng để tạo một ô trong bảng. Nó có thể chứa một hoặc nhiều thẻ <p>. Nó khác với thẻ <td> ở chỗ nó được sử dụng để tạo một ô tiêu đề trong bảng. Ví dụ: <th> This is a table header </th>
ảnh minh hoạ 7

  • <img> là một thẻ nội tuyến, nó được sử dụng để chèn một hình ảnh vào trang web. Nó không có thẻ đóng tương ứng. Ví dụ: <img src="image.jpg" alt="Image">
<img src="url" alt="alternatetext">
  • <video> là một thẻ khối, nó được sử dụng để chèn một video vào trang web. Nó có thể chứa một hoặc nhiều thẻ <source>. Ví dụ: <video> This is a video </video>
  • <source> là một thẻ nội tuyến, nó được sử dụng để chỉ định một nguồn video. Nó không có thẻ đóng tương ứng. Ví dụ: <source src="video.mp4" type="video/mp4">
  • <audio> là một thẻ khối, nó được sử dụng để chèn một âm thanh vào trang web. Nó có thể chứa một hoặc nhiều thẻ <source>. Ví dụ: <audio> This is an audio </audio>
  • <source> là một thẻ nội tuyến, nó được sử dụng để chỉ định một nguồn âm thanh. Nó không có thẻ đóng tương ứng. Ví dụ: <source src="audio.mp3" type="audio/mp3">
  • <a> là một thẻ nội tuyến, nó được sử dụng để tạo một liên kết đến một trang web khác. Nó có thể chứa một hoặc nhiều thẻ <img>. Ví dụ: <a href="https://www.google.com"> This is a link </a>
<a href="url">link text</a>
  • attributes quan trọng nhất của thẻ là “href”, nó cho biết đích đến của link.
  • Target attribute :
    • _self : mở tài liệu cùng cửa sổ khi nhấn link.
    • _blank : mở tài liệu ở cửa sổ mới.
    • _parent : mở tài liệu trong khung chính.
    • _top : mở tài liệu trong toàn bộ cửa sổ.

VI. Section elements: div, span, header, footer, nav, main, section, article

https://www.w3schools.com/html/img_sem_elements.gif
  • <div> là một thẻ khối, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <div> This is a div </div>
  • <span> là một thẻ nội tuyến, nó được sử dụng để nhóm các phần tử HTML lại với nhau. Nó không có bất kỳ ý nghĩa đặc biệt nào. Nó thường được sử dụng để tạo các nhóm phần tử CSS để áp dụng các kiểu CSS cho các phần tử đó. Ví dụ: <span> This is a span </span>
  • <header> là một thẻ khối, nó được sử dụng để tạo một tiêu đề cho trang web hoặc một phần của trang web. Ví dụ: <header> This is a header </header>
  • <footer> là một thẻ khối, nó được sử dụng để tạo một chân trang cho trang web hoặc một phần của trang web. Ví dụ: <footer> This is a footer </footer>
  • <nav> là một thẻ khối, nó được sử dụng để tạo một thanh điều hướng cho trang web hoặc một phần của trang web. Ví dụ: <nav> This is a nav </nav>
  • <main> là một thẻ khối, nó được sử dụng để tạo một phần chính của trang web. Ví dụ: <main> This is a main </main>
  • <section> là một thẻ khối, nó được sử dụng để tạo một phần của trang web. Ví dụ: <section> This is a section </section>
  • <article> là một thẻ khối, nó được sử dụng để tạo một bài viết. Ví dụ: <article> This is an article </article>

VII. HTML text fundamentals: h1 -> h6, p, ul, li

ảnh minh hoạ 8
  • <h1> là một thẻ khối, nó được sử dụng để tạo một tiêu đề lớn nhất cho trang web hoặc một phần của trang web. Ví dụ: <h1> This is a heading </h1>
  • <h2> là một thẻ khối, nó được sử dụng để tạo một tiêu đề lớn hơn <h1> cho trang web hoặc một phần của trang web. Ví dụ: <h2> This is a heading </h2>
  • <p> là một thẻ khối, nó được sử dụng để tạo một đoạn văn bản. Ví dụ: <p> This is a paragraph </p>
  • <ul> là một thẻ khối, nó được sử dụng để tạo một danh sách không có thứ tự. Nó có thể chứa một hoặc nhiều thẻ <li>. Ví dụ: <ul> This is an unordered list </ul>
  • <li> là một thẻ khối, nó được sử dụng để tạo một mục trong danh sách. Nó có thể chứa một hoặc nhiều thẻ <p>. Ví dụ: <li> This is a list item </li>
  • <ol> là một thẻ khối, nó được sử dụng để tạo một danh sách có thứ tự. Nó có thể chứa một hoặc nhiều thẻ <li>. Ví dụ: <ol> This is an ordered list </ol>
ảnh minh hoạ 9

VIII. Các thẻ liên quan đến form: form, input, button, label, textarea

  • <form> là một thẻ khối, nó được sử dụng để tạo một biểu mẫu. Nó có thể chứa một hoặc nhiều thẻ <input><button><label><textarea>. Ví dụ: <form> This is a form </form>
  • <input> là một thẻ nội tuyến, nó được sử dụng để tạo một trường nhập liệu. Nó không có thẻ đóng tương ứng. Ví dụ: <input type="text">
  • <button> là một thẻ nội tuyến, nó được sử dụng để tạo một nút. Nó có thể chứa một hoặc nhiều thẻ <img>. Ví dụ: <button> This is a button </button>
  • <label> là một thẻ nội tuyến, nó được sử dụng để tạo một nhãn cho một trường nhập liệu. Nó có thể chứa một hoặc nhiều thẻ <input>. Ví dụ: <label> This is a label </label>
  • <textarea> là một thẻ khối, nó được sử dụng để tạo một trường nhập liệu dài. Nó không có thẻ đóng tương ứng. Ví dụ: <textarea> This is a textarea </textarea>
  • <select> là một thẻ khối, nó được sử dụng để tạo một trường nhập liệu dạng danh sách thả xuống. Nó có thể chứa một hoặc nhiều thẻ <option>. Ví dụ: <select> This is a select </select>
  • <option> là một thẻ khối, nó được sử dụng để tạo một mục trong danh sách thả xuống. Nó không có thẻ đóng tương ứng. Ví dụ: <option> This is an option </option>
ảnh minh hoạ 11

IX. Có thể tìm hiểu các thẻ khác ngoài các thẻ trên các thẻ trên

  • <br> : Xuống dòng
  • <hr> : Thẻ ngang
  • <i> : In nghiêng
  • <b> : In đậm