Mẫu thiết kế để ghi nhật ký xung quanh thực thi


11

Giới thiệu

Tôi đang triển khai một lớp Java trừu tượng của một khung xử lý *. Bằng cách thực hiện chức năng execute, tôi có thể thêm chức năng logic kinh doanh. Tôi muốn thêm ghi nhật ký vào lúc bắt đầu và kết thúc mỗi lần thực hiện mọi chức năng thực thi của mình. Cũng ở giữa một số đăng nhập, nếu những điều cụ thể được thực hiện. Tuy nhiên, tôi muốn làm điều này một cách thống nhất. Suy nghĩ của tôi là kế thừa lớp khung thành một lớp mới riêng để thực hiện ghi nhật ký và cung cấp một số executeWithLoggingchức năng mới , bao bọc việc ghi nhật ký xung quanh các phần cụ thể. Tôi không chắc đó có phải là ý tưởng tốt nhất hay không và liệu tôi có thể sử dụng một mẫu thiết kế sẽ làm cho toàn bộ nỗ lực trở nên thanh lịch hay không. Làm thế nào tôi sẽ tiếp tục về tất cả mọi thứ?

Những thách thức (xin vui lòng xem xét những điều này!)

  1. Một thách thức trong trường hợp của tôi là lớp ban đầu chỉ có một executechức năng, tuy nhiên, tôi cần đăng nhập xung quanh nhiều phần.
  2. Điều thứ hai là: Sử dụng một mẫu trang trí (cũng là ý tưởng đầu tiên của tôi) không hoàn toàn hiệu quả, nếu tôi cũng đang sử dụng một execute, bởi vì super.execute()chức năng sẽ phải được gọi trong dòng đầu tiên của cái mới của tôi execute(), phải không ?
  3. Sẽ có ít nhất 4 lớp tham gia: BaseFunctiontừ khung, LoggingFunction extends BaseFunctiontừ tôi, MyBusinessFunction extends LoggingFunctiontừ tôi, MyBusinessClassmà bắt nguồn MyBusinessFunction.
  4. Tôi không chỉ cần đăng nhập vào đầu và cuối execute, mà còn ở giữa.
  5. Việc "ghi nhật ký" không chỉ là ghi nhật ký Java đơn giản, mà thực sự là ghi nhật ký vào cơ sở dữ liệu. Điều này không thay đổi bất cứ điều gì về các nguyên tắc, nhưng chứng minh rằng việc ghi nhật ký có thể không chỉ là một dòng mã.

Có lẽ một ví dụ về cách tôi làm toàn bộ mọi thứ sẽ tốt đẹp, để đưa tôi đi.

| * Chức năng Storm Trident, tương tự như Storm Storm, nhưng điều này không đặc biệt quan trọng.


1
super.execute được gọi bởi người trang trí, không phải người trang trí. Lớp chính của bạn thậm chí không nhận ra nó đang được trang trí và bạn chỉ cần cẩn thận với các cuộc gọi này, vì chúng không thể bao gồm người trang trí (trái ngược với cách tiếp cận với mixin hoặc đặc điểm).
Frank

executetrong BaseFunctiontrừu tượng?
Phát hiện

@ Spiated: Có, executelà trừu tượng trong BaseFunction.
Make42

Câu trả lời:


13

Nhìn chung có một số cách có thể để thực hiện đăng nhập xung quanh một thực thi nhất định. Trước hết, thật tốt khi bạn muốn tách riêng điều này, vì đăng nhập là một ví dụ điển hình cho Tách biệt các mối quan tâm . Thật không có ý nghĩa gì khi thêm đăng nhập trực tiếp vào logic kinh doanh của bạn.

Đối với các mẫu thiết kế có thể, đây là danh sách (không kết luận) từ đỉnh đầu của tôi:

  • Mẫu trang trí : Một mẫu thiết kế nổi tiếng, về cơ bản, bạn bao bọc lớp không ghi nhật ký vào một lớp khác có cùng giao diện và trình bao bọc thực hiện ghi nhật ký trước và / hoặc sau khi gọi lớp được bọc.

  • Thành phần chức năng : Trong ngôn ngữ lập trình chức năng, bạn có thể chỉ cần kết hợp chức năng của mình với chức năng ghi nhật ký. Điều này tương tự như mẫu trang trí trong các ngôn ngữ OO, nhưng thực sự không phải là một cách ưa thích, vì chức năng ghi nhật ký tổng hợp sẽ dựa trên việc triển khai hiệu ứng phụ.

  • Monads : Monads cũng đến từ thế giới lập trình chức năng và cho phép bạn tách rời việc thực thi và đăng nhập. Điều này về cơ bản có nghĩa là bạn tổng hợp các thông điệp ghi nhật ký và phương thức thực thi cần thiết, nhưng chưa thực thi. Sau đó, bạn có thể xử lý đơn nguyên để thực hiện ghi nhật ký và / hoặc thực thi logic nghiệp vụ.

  • Lập trình hướng theo khía cạnh : Cách tiếp cận tinh vi hơn, đạt được sự tách biệt hoàn toàn, vì lớp thực hiện hành vi không ghi nhật ký không bao giờ tương tác trực tiếp với việc ghi nhật ký.

  • Chuyển tiếp: Các mẫu thiết kế tiêu chuẩn khác cũng có thể phù hợp, ví dụ, Mặt tiền có thể đóng vai trò là điểm nhập đã ghi, trước khi chuyển tiếp cuộc gọi đến một lớp khác thực hiện phần logic nghiệp vụ. Điều này có thể được áp dụng cho các mức độ trừu tượng khác nhau là tốt. Ví dụ: bạn có thể ghi nhật ký các yêu cầu được thực hiện vào một URL dịch vụ có thể truy cập bên ngoài, trước khi chuyển tiếp đến một URL nội bộ để xử lý các yêu cầu không được ghi nhật ký thực tế.

Đối với yêu cầu bổ sung của bạn rằng bạn cần thực hiện đăng nhập "ở giữa" - điều đó có thể chỉ ra một thiết kế lạ. Tại sao nó, mà thực thi của bạn đang làm rất nhiều, mà một cái gì đó như là "giữa" của thực thi của nó thậm chí còn được quan tâm từ xa? Nếu thực sự có rất nhiều thứ đang diễn ra, vậy thì tại sao bạn không tập hợp nó thành các giai đoạn / giai đoạn / những gì bạn có? Về lý thuyết, bạn có thể đạt được việc đăng nhập ở giữa với AOP tôi đoán, nhưng tôi vẫn cho rằng một sự thay đổi thiết kế dường như phù hợp hơn nhiều.


3

Tôi có cảm giác rằng bạn hoàn toàn muốn sử dụng một mô hình. Hãy nhớ rằng việc sử dụng sai các mẫu thiết kế có thể dẫn đến một mã ít có thể duy trì / dễ đọc hơn.

Con ong đó nói ...

Trường hợp của bạn là khó khăn vì có vẻ như bạn muốn thực hiện 2 loại đăng nhập:

  1. Ghi nhật ký một số bước của chức năng logic của bạn (bên trong execute)
  2. Ghi nhật ký xung quanh các lệnh gọi hàm (nhập / thoát một hàm) (bên ngoài execute)

Trong trường hợp đầu tiên, tại thời điểm bạn muốn đăng nhập một số thứ bên trong một hàm, bạn phải thực hiện nó ... bên trong hàm. Bạn có thể sử dụng mẫu phương thức mẫu để làm cho mọi thứ đẹp hơn một chút nhưng tôi nghĩ nó quá mức trong trường hợp này.

public final class MyBusinessFunction extends BaseFunction {
    @Override
    public final void execute() {
        log.info("Initializing some variables");
        int i = 0;
        double d = 1.0;

        log.info("Starting algorithm");
        for(...) {
            ...
            log.info("One step forward");
            ...
        }
        log.info("Ending algorithm");

        ...
        log.info("Cleaning some mess");
        ...
    }
}

Đối với trường hợp thứ hai, mẫu trang trí là cách để đi:

public final class LoggingFunction extends BaseFunction {
    private final BaseFunction origin;

    public LoggingFunction(BaseFunction origin) {
        this.origin = origin;
    }

    @Override
    public final void execute() {
        log.info("Entering execute");
        origin.execute();
        log.info("Exiting execute");
    }
}

Và bạn có thể sử dụng nó như thế này trong BusinessClass:

public final BusinessClass {
    private final BaseFunction f1;

    public BusinessClass() {
        this.f1 = new LoggingFunction(new MyBusinessFunction());
    }

    public final void doFunction() {
        f1.execute();
    }
}

Một cuộc gọi đến doFunctionsẽ đăng nhập này:

Entering execute
Initializing some variables
Starting algorithm
One step forward
One step forward
...
Ending algorithm
Cleaning some mess
Exiting execute

Nó sẽ làm việc để bỏ qua thuộc tính gốc và viết super.execute();thay vì origin.execute();?
Thực hiện 42

Giải pháp của bạn chưa hoàn thành - có thể do tôi giải thích không tốt. Tôi sẽ thêm thông tin trong câu hỏi.
Make42

@ user49283 Tại sao bạn muốn gọi super.execute()như phương thức này abstract?
Phát hiện

@ user49283 Tôi cũng đã cập nhật câu trả lời của mình
Phát hiện

Tôi chưa hoàn toàn hiểu mẫu, nhưng tôi sẽ sớm thực hiện lại. Trong khi chờ đợi: Làm thế nào về việc sử dụng Mẫu lệnh? Tôi đã đưa ra câu trả lời bằng Mẫu lệnh - vui lòng nhận xét!
Make42

1

Cách tiếp cận của bạn có vẻ hợp lệ và thực sự là một triển khai của Mẫu trang trí . Có nhiều cách khác bạn có thể làm điều này tôi cho rằng, nhưng Decorator là một mẫu rất thanh lịch và dễ hiểu, rất phù hợp để giải quyết vấn đề của bạn.


0

Làm thế nào về việc sử dụng Mẫu lệnh?

abstract class MyLoggingCommandPart {

  MyLoggingCommandPart() {
    log.info("Entering execute part");
    executeWithoutLogging();
    log.info("Exiting execute part");
  }

  abstract void executeWithoutLogging();

}

abstract class MyLoggingCommandWhole {

  MyLoggingCommandWhole() {
    log.info("Entering execute whole");
    executeWithoutLogging();
    log.info("Exiting execute whole");
  }

  abstract void executeWithoutLogging();

}

public final class MyBusinessFunction extends BaseFunction {

    @Override
    public final void execute() {

        new MyLoggingCommandWhole(){

            @Override
            executeWithoutLogging(){

                new MyLoggingCommandPart(){
                    @Override
                    executeWithoutLogging(){
                    // ... what I actually wanne do
                    }
                }

                new MyLoggingCommandPart(){
                    @Override
                    executeWithoutLogging(){
                    // ... some more stuff I actually wanne do
                    }
                }
            }
        }
    }
}

Tôi không có nhiều kinh nghiệm với mẫu lệnh. Điểm trừ tiêu cực rõ ràng duy nhất tôi thấy là nó khó đọc (lưu ý mức độ thụt), vì vậy tôi sẽ không sử dụng nó vì tôi thích quan tâm đến những người sẽ đọc mã của tôi trong tương lai.
Phát hiện

Cũng execute()không làm gì vào lúc này vì bạn chỉ đang kích hoạt MyLoggingCommandWhole.
Phát hiện

@ Phát hiện: Tôi nghĩ rằng thấm nhuần MyLoggingCommandWhole, thực thi executeWithoutLogging();từ MyLoggingCommandWhole. đây không phải là trường hợp à?
Make42

Không, không phải vậy. Bạn sẽ phải viết một cái gì đó như:MyLoggingCommandWhole cmd = new MyLoggingCommandWhole() { ... }; cmd.executeWithoutLogging();
Phát hiện

@ Phát hiện: Điều đó hầu như không có ý nghĩa. Nếu đúng như bạn nói, tôi muốn đặt mã từ hàm tạo vào một hàm như executeWithLogging()cmd.executeWithLogging(). Sau tất cả, đó là những gì nên được thực hiện.
Make42
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.