Việc đăng nhập bên cạnh việc triển khai có vi phạm SRP không?


19

Khi nghĩ đến việc phát triển phần mềm nhanh và tất cả các nguyên tắc (SRP, OCP, ...) tôi tự hỏi mình cách xử lý đăng nhập.

Việc đăng nhập bên cạnh việc triển khai có vi phạm SRP không?

Tôi sẽ nói yesbởi vì việc thực hiện cũng có thể chạy mà không cần đăng nhập. Vì vậy, làm thế nào tôi có thể thực hiện đăng nhập một cách tốt hơn? Tôi đã kiểm tra một số mẫu và đưa ra kết luận rằng cách tốt nhất để không vi phạm các nguyên tắc theo cách do người dùng xác định, nhưng sử dụng bất kỳ mẫu nào được biết là vi phạm nguyên tắc là sử dụng mẫu trang trí.

Giả sử chúng ta có một loạt các thành phần hoàn toàn không có vi phạm SRP và sau đó chúng tôi muốn thêm ghi nhật ký.

  • thành phần A
  • thành phần B sử dụng A

Chúng tôi muốn đăng nhập cho A, vì vậy chúng tôi tạo một thành phần D khác được trang trí bằng A cả hai thực hiện giao diện I.

  • giao diện tôi
  • thành phần L (thành phần đăng nhập của hệ thống)
  • thành phần A thực hiện I
  • thành phần D thực hiện I, trang trí / sử dụng A, sử dụng L để ghi nhật ký
  • thành phần B sử dụng I

Ưu điểm: - Tôi có thể sử dụng A mà không cần đăng nhập - thử nghiệm A có nghĩa là tôi không cần bất kỳ giả lập đăng nhập nào - các thử nghiệm đơn giản hơn

Nhược điểm: - nhiều thành phần hơn và nhiều thử nghiệm hơn

Tôi biết đây có vẻ là một câu hỏi thảo luận mở khác, nhưng tôi thực sự muốn biết liệu ai đó sử dụng chiến lược ghi nhật ký tốt hơn so với vi phạm trang trí hay vi phạm SRP. Điều gì về logger singleton tĩnh như là NullLogger mặc định và nếu muốn ghi nhật ký nhật ký hệ thống, người ta sẽ thay đổi đối tượng thực hiện trong thời gian chạy?



Tôi đã đọc nó và câu trả lời là không thỏa mãn, xin lỗi.
Aitch


@MarkRogers cảm ơn bạn đã chia sẻ bài viết thú vị đó. Chú Bob nói trong 'Clean Code', rằng một thành phần SRP đẹp đang xử lý các thành phần khác có cùng mức độ trừu tượng. Đối với tôi, lời giải thích dễ hiểu hơn vì bối cảnh cũng có thể quá lớn. Nhưng tôi không thể trả lời câu hỏi, bởi vì bối cảnh hoặc sự trừu tượng của một logger là gì?
Aitch

3
"Không phải là một câu trả lời cho tôi" hoặc "câu trả lời không thỏa mãn" là một chút bác bỏ. Bạn có thể suy nghĩ cụ thể điều gì không thỏa mãn (bạn có yêu cầu gì mà không được đáp ứng bởi câu trả lời đó? Điều gì đặc biệt về câu hỏi của bạn?), Sau đó chỉnh sửa câu hỏi của bạn để đảm bảo rằng khía cạnh yêu cầu / duy nhất này được giải thích rõ ràng. Mục đích là để giúp bạn chỉnh sửa câu hỏi của mình để cải thiện câu hỏi để làm cho nó rõ ràng và tập trung hơn, không yêu cầu bản tóm tắt khẳng định rằng câu hỏi của bạn khác / không nên đóng mà không có lý do tại sao. (Bạn cũng có thể nhận xét về câu trả lời khác.)
DW

Câu trả lời:


-1

Có, đó là vi phạm SRP vì đăng nhập là mối quan tâm xuyên suốt.

Cách chính xác là ủy quyền đăng nhập vào một lớp logger (Đánh chặn), mục đích duy nhất là đăng nhập - tuân thủ SRP.

Xem liên kết này để biết ví dụ hay: https://msdn.microsoft.com/en-us/l Library / dn178467% 28v = pandp.30% 29.aspx

Đây là một ví dụ ngắn :

public interface ITenantStore
{
    Tenant GetTenant(string tenant);
    void SaveTenant(Tenant tenant);
}

public class TenantStore : ITenantStore
{
    public Tenant GetTenant(string tenant)
    {....}

    public void SaveTenant(Tenant tenant)
    {....}
} 

public class TenantStoreLogger : ITenantStore
{
    private readonly ILogger _logger; //dep inj
    private readonly ITenantStore _tenantStore;

    public TenantStoreLogger(ITenantStore tenantStore)
    {
        _tenantStore = tenantStore;
    }

    public Tenant GetTenant(string tenant)
    {
        _logger.Log("reading tenant " + tenant.id);
        return _tenantStore.GetTenant(tenant);
    }

    public void SaveTenant(Tenant tenant)
    {
        _tenantStore.SaveTenant(tenant);
        _logger.Log("saving tenant " + tenant.id);
    }
}

Lợi ích bao gồm

  • Bạn có thể kiểm tra điều này mà không cần đăng nhập - kiểm tra đơn vị thực sự
  • bạn có thể dễ dàng bật / tắt đăng nhập - ngay cả khi chạy
  • bạn có thể thay thế ghi nhật ký cho các hình thức đăng nhập khác mà không cần phải thay đổi tệp TenantStore.

Cảm ơn bạn đã liên kết tốt đẹp. Hình 1 trên trang đó thực sự là cái mà tôi sẽ gọi là giải pháp yêu thích của mình. Danh sách các mối quan tâm xuyên suốt (ghi nhật ký, lưu vào bộ nhớ cache, v.v.) và mẫu trang trí là giải pháp chung nhất và tôi rất vui khi không hoàn toàn sai với suy nghĩ của mình, mặc dù cộng đồng lớn hơn muốn loại bỏ tính trừu tượng và ghi nhật ký nội tuyến .
Aitch

2
Tôi không thấy bạn gán biến _logger ở bất cứ đâu. Bạn đã có kế hoạch sử dụng tiêm constructor và chỉ quên? Nếu vậy, bạn có thể nhận được một cảnh báo trình biên dịch.
2023861

27
Thay vì TenantStore được nhúng với Logger mục đích chung, yêu cầu các lớp N + 1 (khi bạn thêm LandlordStore, FooStore, BarStore, v.v.), bạn có TenantStoreLogger được nhúng với TenantStore, FooStoreLogger , v.v ... yêu cầu các lớp 2N. Theo như tôi có thể nói, vì lợi ích không. Khi bạn muốn thực hiện kiểm tra đơn vị không ghi nhật ký, bạn sẽ cần phải khởi động lại các lớp N, thay vì chỉ định cấu hình NullLogger. IMO, đây là một cách tiếp cận rất kém.
user949300 11/03/2015

6
Làm điều này cho mọi lớp duy nhất cần ghi nhật ký làm tăng đáng kể độ phức tạp của cơ sở mã của bạn (trừ khi có rất ít lớp ghi nhật ký mà bạn thậm chí sẽ không gọi nó là mối quan tâm xuyên suốt nữa). Cuối cùng, nó làm cho mã ít được bảo trì hơn đơn giản chỉ vì số lượng lớn các giao diện cần duy trì, đi ngược lại mọi thứ mà Nguyên tắc Trách nhiệm duy nhất được tạo ra.
jpmc26

9
Bị hạ bệ. Bạn đã xóa mối quan tâm đăng nhập khỏi lớp Người thuê, nhưng bây giờ bạn TenantStoreLoggersẽ thay đổi mỗi khi TenantStorethay đổi. Bạn không tách rời mối quan tâm nhiều hơn trong giải pháp ban đầu.
Laurent LA RIZZA

61

Tôi muốn nói rằng bạn đang dùng SRP quá nghiêm túc. Nếu mã của bạn đủ gọn gàng để đăng nhập là "vi phạm" duy nhất của SRP thì bạn đang làm tốt hơn 99% tất cả các lập trình viên khác và bạn nên vỗ nhẹ vào lưng.

Quan điểm của SRP là tránh mã spaghetti khủng khiếp trong đó mã làm những việc khác nhau được trộn lẫn với nhau. Trộn ghi nhật ký với mã chức năng không đổ chuông báo động cho tôi.


19
@Aitch: Lựa chọn của bạn là kết nối chặt chẽ việc đăng nhập vào lớp của bạn, chuyển một tay cầm cho một logger hoặc không đăng nhập bất cứ thứ gì cả. Nếu bạn cực kỳ nghiêm ngặt về SRP với chi phí của mọi thứ khác, tôi khuyên bạn không nên đăng nhập bất cứ điều gì, bao giờ. Bất cứ điều gì bạn cần biết về những gì phần mềm của bạn đang làm đều có thể được xử lý bằng trình gỡ lỗi. Chữ P trong SRP là viết tắt của "nguyên tắc", chứ không phải "quy luật vật lý của tự nhiên không bao giờ bị phá vỡ".
Blrfl

3
@Aitch: Bạn sẽ có thể theo dõi việc đăng nhập trong lớp của bạn trở lại một số yêu cầu, nếu không bạn đang vi phạm YAGNI. Nếu ghi nhật ký trên bàn, bạn cung cấp một trình xử lý logger hợp lệ giống như bạn làm với bất kỳ thứ gì khác mà lớp cần, tốt nhất là một từ một lớp đã vượt qua kiểm tra. Cho dù đó là một trong đó tạo ra các mục nhật ký thực tế hoặc bỏ chúng vào thùng bit là mối quan tâm của việc khởi tạo thể hiện của lớp của bạn; lớp học không nên quan tâm.
Blrfl

3
@Aitch Để trả lời câu hỏi của bạn về kiểm tra đơn vị : Do you mock the logger?, đó là CHÍNH XÁC những gì bạn làm. Bạn nên có một ILoggergiao diện xác định CÁI GÌ. Mã được kiểm tra sẽ được thêm vào một mã ILoggermà bạn chỉ định. Để thử nghiệm, bạn có class TestLogger : ILogger. Điều tuyệt vời về điều này là TestLoggercó thể phơi bày những thứ như chuỗi cuối cùng hoặc lỗi được ghi lại. Các thử nghiệm có thể xác minh rằng mã đang thử nghiệm đang ghi nhật ký chính xác. Ví dụ, một bài kiểm tra có thể là UserSignInTimeGetsLogged(), trong đó bài kiểm tra kiểm tra TestLoggercho bản ghi.
CurtisHx 11/03/2015

5
99% có vẻ hơi thấp. Bạn có thể tốt hơn 100% tất cả các lập trình viên.
Paul Draper

2
+1 cho sự tỉnh táo. Chúng ta cần nhiều hơn những kiểu suy nghĩ này: ít tập trung vào các từ và các nguyên tắc trừu tượng và tập trung nhiều hơn vào việc có một cơ sở mã có thể duy trì .
jpmc26

15

Không, đó không phải là vi phạm SRP.

Các tin nhắn bạn gửi đến nhật ký sẽ thay đổi vì những lý do tương tự như mã xung quanh.

Điều vi phạm SRP là gì khi sử dụng một thư viện cụ thể để đăng nhập trực tiếp vào mã. Nếu bạn quyết định thay đổi cách đăng nhập, SRP tuyên bố rằng nó sẽ không ảnh hưởng đến mã doanh nghiệp của bạn.

Một số loại tóm tắt Loggernên có thể truy cập được vào mã triển khai của bạn và điều duy nhất mà việc triển khai của bạn nên nói là "Gửi thông điệp này đến nhật ký", không có vấn đề gì về cách thức thực hiện. Quyết định về cách ghi nhật ký chính xác (thậm chí là đánh dấu thời gian) không phải là trách nhiệm của bạn.

Việc triển khai của bạn sau đó cũng không nên biết liệu logger mà nó đang gửi tin nhắn đến là a NullLogger.

Mà nói.

Tôi sẽ không đăng nhập đi vì mối quan tâm xuyên suốt quá nhanh . Phát ra nhật ký để theo dõi các sự kiện cụ thể xảy ra trong mã triển khai của bạn thuộc về mã thực hiện.

Một mối quan tâm xuyên suốt, OTOH, là theo dõi thực thi : đăng nhập vào và thoát trong mỗi phương thức. AOP là nơi tốt nhất để làm điều này.


Giả sử thông điệp logger là 'người dùng đăng nhập xyz', được gửi đến một trình ghi nhật ký có dấu thời gian, v.v. Bạn có biết 'đăng nhập' nghĩa là gì khi thực hiện không? Là nó bắt đầu một phiên với một cookie hoặc bất kỳ cơ chế nào khác? Tôi nghĩ rằng có nhiều cách khác nhau để thực hiện đăng nhập, do đó thay đổi việc triển khai không hợp lý với thực tế là người dùng đăng nhập. Đó là một ví dụ tuyệt vời khác về trang trí các thành phần khác nhau (giả sử OAuthLogin, SessionLogin, BasicAuthorizationLogin) làm điều tương tự như một Logingiao diện được trang trí với cùng một logger.
Aitch

Nó phụ thuộc vào thông điệp "đăng nhập người dùng xyz" nghĩa là gì. Nếu nó đánh dấu sự thật rằng đăng nhập thành công, việc gửi tin nhắn đến nhật ký thuộc về trường hợp sử dụng đăng nhập. Cách cụ thể để biểu thị thông tin đăng nhập dưới dạng chuỗi (OAuth, Phiên, LDAP, NTLM, dấu vân tay, bánh xe hamster) thuộc về lớp cụ thể đại diện cho thông tin đăng nhập hoặc chiến lược đăng nhập. Không có nhu cầu hấp dẫn để loại bỏ nó. Trường hợp riêng biệt này không phải là một mối quan tâm xuyên suốt. Nó là cụ thể cho trường hợp sử dụng đăng nhập.
Laurent LA RIZZA

7

Vì việc ghi nhật ký thường được coi là mối quan tâm xuyên suốt, tôi khuyên bạn nên sử dụng AOP để tách việc ghi nhật ký khỏi thực hiện.

Tùy thuộc vào ngôn ngữ bạn sử dụng một bộ chặn hoặc một số khung AOP (ví dụ AspectJ trong Java) để thực hiện điều này.

Câu hỏi là nếu điều này thực sự có giá trị rắc rối. Lưu ý rằng sự tách biệt này sẽ làm tăng sự phức tạp của dự án của bạn trong khi cung cấp rất ít lợi ích.


2
Hầu hết các mã AOP mà tôi thấy là về việc đăng nhập mọi bước vào và thoát của mọi phương thức. Tôi chỉ muốn đăng nhập một số phần logic kinh doanh. Vì vậy, có thể chỉ có thể ghi nhật ký các phương thức có chú thích, nhưng AOP hoàn toàn chỉ có thể tồn tại trong các ngôn ngữ kịch bản và môi trường máy ảo, phải không? Ví dụ như C ++ thì không thể. Tôi thừa nhận rằng tôi không hài lòng lắm với các phương pháp AOP, nhưng có lẽ không có giải pháp nào sạch hơn.
Aitch

1
@Aitch. "C ++ không thể." : Nếu bạn google cho "aop c ++", bạn sẽ tìm thấy các bài viết về nó. "... mã AOP mà tôi thấy là về việc ghi nhật ký mọi bước vào và thoát của mọi phương thức. Tôi chỉ muốn đăng nhập một số phần logic nghiệp vụ." Aop cho phép bạn xác định các mẫu để tìm các phương thức để sửa đổi. tức là tất cả các phương thức từ không gian tên "my.busininess. *"
k3b 11/03/2015

1
Ghi nhật ký thường KHÔNG phải là mối quan tâm xuyên suốt, đặc biệt là khi bạn muốn nhật ký của mình chứa thông tin thú vị, tức là nhiều thông tin hơn trong một dấu vết ngăn xếp ngoại lệ.
Laurent LA RIZZA

5

Điều này nghe có vẻ tốt. Bạn đang mô tả một trang trí khai thác gỗ khá chuẩn. Bạn có:

thành phần L (thành phần đăng nhập của hệ thống)

Điều này có một trách nhiệm: đăng nhập thông tin được truyền cho nó.

thành phần A thực hiện I

Điều này có một trách nhiệm: cung cấp việc triển khai giao diện I (giả sử tôi tuân thủ đúng SRP).

Đây là phần quan trọng:

thành phần D thực hiện I, trang trí / sử dụng A, sử dụng L để ghi nhật ký

Khi nói theo cách đó, nghe có vẻ phức tạp, nhưng hãy nhìn nó theo cách này: Thành phần D thực hiện một điều: mang A và L lại với nhau.

  • Thành phần D không đăng nhập; nó ủy thác cho L
  • Thành phần D không tự thực hiện; nó ủy thác cho A

Các chỉ trách nhiệm mà thành phần D có là để đảm bảo rằng L được thông báo khi A được sử dụng. Việc triển khai A và L đều ở những nơi khác. Điều này hoàn toàn tuân thủ SRP, cũng như là một ví dụ gọn gàng của OCP và việc sử dụng trang trí khá phổ biến.

Một cảnh báo quan trọng: khi D sử dụng thành phần ghi nhật ký L của bạn, nó sẽ thực hiện theo cách cho phép bạn thay đổi cách đăng nhập. Cách đơn giản nhất để thực hiện việc này là có một giao diện IL được triển khai bởi L. Sau đó:

  • Thành phần D sử dụng IL để đăng nhập; một trường hợp của L được cung cấp
  • Hợp phần D sử dụng I để cung cấp chức năng; một trường hợp của A được cung cấp
  • Thành phần B sử dụng I; một thể hiện của D được cung cấp

Bằng cách đó, không có gì phụ thuộc trực tiếp vào bất cứ điều gì khác, giúp bạn dễ dàng trao đổi chúng. Điều này giúp dễ dàng thích ứng với thay đổi và dễ dàng giả định các bộ phận của hệ thống để bạn có thể kiểm tra đơn vị.


Tôi thực sự chỉ biết C # có hỗ trợ phái đoàn bản địa. Đó là lý do tại sao tôi viết D implements I. Cảm ơn về câu trả lời của bạn.
Aitch

1

Tất nhiên đó là vi phạm SRP vì bạn có mối quan tâm xuyên suốt. Tuy nhiên, bạn có thể tạo một lớp chịu trách nhiệm soạn nhật ký với việc thực hiện bất kỳ hành động nào.

thí dụ:

class Logger {
   ActuallLogger logger;
   public Action ComposeLog(string msg, Action action) {
      return () => {
          logger.debug(msg);
          action();
      };
   }
}

2
Bị hạ bệ. Ghi nhật ký là một mối quan tâm xuyên suốt thực sự. Vì vậy, là trình tự phương thức gọi trong mã của bạn. Điều đó không đủ lý do để tuyên bố vi phạm SRP. Ghi nhật ký sự xuất hiện của một sự kiện cụ thể trong ứng dụng của bạn KHÔNG phải là mối quan tâm xuyên suốt. CÁCH những thông điệp này được mang đến cho bất kỳ người dùng quan tâm nào thực sự là một mối quan tâm riêng biệt và việc mô tả điều này trong mã thực thi LÀ vi phạm SRP.
Laurent LA RIZZA

"Các cuộc gọi phương thức giải trình tự" hoặc thành phần chức năng không phải là mối quan tâm xuyên suốt mà là một chi tiết triển khai. Trách nhiệm của hàm tôi đã tạo là soạn một câu lệnh log, với một hành động. Tôi không cần sử dụng từ "và" để mô tả chức năng này làm gì.
Paul Nikonowicz

Đây không phải là một chi tiết thực hiện. Nó có ảnh hưởng sâu sắc đến hình dạng mã của bạn.
Laurent LA RIZZA

Tôi nghĩ rằng tôi đang xem xét SRP từ góc độ "Chức năng này làm gì" khi bạn đang xem SRP từ góc độ "LÀM THẾ NÀO để thực hiện chức năng này".
Paul Nikonowicz
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.