Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về Adater Design Pattern trong Scala
Adapter Design Pattern là gì?
Mục đích của Pattern
- Chuyển đổi interface của class thành interface khác mà client mong muốn. Adapter cho phép các class làm việc cùng nhau mà không gặp vấn đề gì liên quan các interface không tương thích.
- Đóng gói lớp hiện tại bằng interface mới
Có 2 kiểu Adapter cơ bản là class adapter và object adapter. Mỗi loại đều có điểm mạnh và điểm yếu khác nhau
Class adapter sử dụng kế thừa để tương thích một interface khác.
Ở đây, một Adapter được kế thừa từ Adaptee, interface đích được biểu diễn bằng class Target. Khi một client gọi một operation trên Target (ví dụ Reuqest()), Adapter class gọi phương thức kế thừa thích hợp từ Adaptee class. Thừa kế cho phép chúng ta cải tiến behavior của Adaptee nếu cần, bằng cách override các phương thức, nhưng nó cũng tạo một phụ thuộc tĩnh trên class Adaptee. Điều này nghĩa là Adapter class không thể sử dụng lại được các subclass của Adapter.
Object Adapter sử dụng composition/delegation thay cho kế thừa như cơ chế để nhận biết interface tương ứng.
Adapter class chứa một tham chiếu đến Adaptee. Khi client gọi phương thức Request(), thông báo được truyền đến Adaptee object. Vì Adapter chỉ chứa một tham chiếu đến Apdaptee, không có phụ thuộc tĩnh nào vào class cụ thể được đưa ra, Adapter class có thể được sử dụng lại với bất kỳ subclass nào của Adaptee và nó làm cho nó không thể override các behaviour của Adapter
Cả 2 cách trên đều cung cấp minh bạch cho tất cả các client, những client mong muốn các interface đích và những Adaptee interface thích hợp
Hãy cùng xem đoạn code bên dưới đây
trait Target { def f } class Adaptee { def g = println("g") } trait Adapter { self: Target with Adaptee => def f = g } def main(args: Array[String]) = { val adapter = new Adaptee with Adapter with Target adapter.f adapter.g ( ) }
Phương thức main() đưa ra cách làm thế nào để tạo một Adapter. Gọi phương thức f và g minh hoạ chúng ta đang xử lý theo Adapter
Kết quả
g g
Cải tiến ghi log dùng Adapter Pattern
Trong rất nhiều các ứng dụng ngày này, thông tin chi tiết về một user cụ thể như là username, địa chỉ IP, số điện thoại… được ghi trực tiếp mà không gây xáo trộn nó. Sau khi luật bảo vệ dữ liệu mới, nó buộc chúng ta phải xử lý xáo trộn các dữ liệu này để không ai hiểu thông tin dữ liệu này là gì.
Do đó chúng ta cần thay đổi logic ghi dữ liệu cho tất cả các ứng dụng này. Mục đích để làm việc này chúng ta sẽ thêm vào một vài phương thức framework ghi log của chúng ta
Chúng ta sẽ thay đổi logger framework hiện tại thành một framework mà hỗ trợ các phương thức như là scrambledInfo(), scambledError(), scrambledDebug()…
Chúng ta sẽ tạo một LoggerAdapter class mà sẽ tương thích hoặc đóng gói một ScrambledLogger và bất cứ khi nào client gọi phương thức info
nó sẽ gọi scrambledInfo
phương thức và sẽ xử lý tương tự với error
và debug
Client là class hiện tại đang gọi phương thức logging
LoggerHelper là một trait hiện có trong đó tất cả các phương thức logging yêu cầu được khai báo
ScrambledLogger là class mới sẽ xáo trộn tất cả các log trước khi ghi vào. Về cơ bản nó chính là Adaptee.
LoggerAdapter là class mà sẽ hoạt động như là một interface giữa Client và Adaptee class ScrambledLogger
import org.apache.log4j.BasicConfigurator object Client extends App { BasicConfigurator.configure() val logger : LoggerHelper = LoggerAdapter.getLogger(this.getClass.getName) logger.info("Log Contains IP address: 127.0.0.1") logger.debug("UserName: jainnancy trying to sign in") logger.error("Password: abxyz is wrong ") }
object LoggerAdapter extends LoggerHelper { var scrambledLogger : ScrambledLogger = _ override def info(msg : String) = scrambledLogger.scrambledInfo(msg) override def debug(msg : String) = scrambledLogger.scrambledDebug(msg) override def error(msg : String) = scrambledLogger.scrambledError(msg) def getLogger(s : String) : LoggerHelper = { scrambledLogger = new ScrambledLogger(s) LoggerAdapter } }
import org.apache.log4j.BasicConfigurator trait LoggerHelper { def info(msg : String) def debug(msg : String) def error(msg : String) }
import org.apache.log4j.Logger class ScrambledLogger(name : String) { private val regex = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b" private val password = "Password: " private val userName = "UserName: " private val logger = Logger.getLogger(name) def scrambledInfo(message : String) = logger.info(scramble(message)) def scrambledDebug(message : String) = logger.debug(scramble(message)) def scrambledError(message : String) = logger.error(scramble(message)) private def scramble(message : String) = scrambleUsername(scrambleIp((scramblePassword(message)))) private def scrambleUsername(message : String) = { if(message.contains(userName)) { val index = message.indexOf(userName) + userName.length() val textStartedPassword = message.substring(index) message.substring(0, index) + "X" + textStartedPassword.substring(textStartedPassword.indexOf(" ")) } else { message } } private def scrambleIp(message : String) = message.replaceAll(regex, "XXX.XXX.XXX.XXX") private def scramblePassword(message : String) = { if(message.contains(password)) { val index = message.indexOf(password) + password.length() val textStartedPassword = message.substring(index) message.substring(0, index) + "X" + textStartedPassword.substring(textStartedPassword.indexOf(" ")) } else { message } } }
Chú ý rằng chúng ta đang sử dụng dependency cho Logger
libraryDependencies += "log4j" % "log4j" % "1.2.17"