Nếu khác - Logic mã lặp lại


15

Ông chủ của tôi đã cho tôi một dự án với một logic cụ thể. Tôi phải phát triển một trang web phải dẫn dắt người điều hướng qua nhiều trường hợp cho đến khi anh ấy / cô ấy đến sản phẩm.

Đây là sơ đồ đường dẫn của điều hướng trong trang web:

Sơ đồ đường dẫn

QUAN TRỌNG!

Trong trang Sản phẩm, người điều hướng có thể chọn bộ lọc nào anh ta muốn.

  • Nếu A, anh ấy / cô ấy PHẢI đi qua B (và sau đó là C) hoặc C và tiếp cận các sản phẩm.
  • Nếu B, anh ấy / cô ấy PHẢI đi qua C và tiếp cận các sản phẩm.
  • Nếu C, anh ấy / cô ấy tiếp cận trực tiếp các sản phẩm.

Tất nhiên, nếu tôi bắt đầu từ AI thì đi theo con đường dài nhất và khi tôi tiếp cận sản phẩm của mình, tôi có 3 bộ lọc hoạt động.

Cho đến bây giờ tôi đã phát triển mã sau đây hoạt động tốt.

if filter_A
  if filter_B
     filter_C()
     .. else ..
  else
     filter_C
    .. else ..
else
   if filter_B
      filter_C()
     .. else ..
   else
     filter_C()
     .. else ..

Tôi ở đây để hỏi một lập trình viên chuyên gia hơn sẽ làm gì trong tình huống này. Tôi đã không tôn trọng nguyên tắc DRY, tôi không thích nó và tôi muốn biết một cách khác để phát triển loại logic này.

Tôi đã nghĩ về việc tách từng phần mã trong các hàm nhưng nó có phải là một ý tưởng tốt trong trường hợp này không?



Sơ đồ luồng điều khiển cho thấy tất cả các điều khiển đi qua filter_C, nhưng các báo cáo có điều kiện chỉ ra rằng luồng điều khiển có thể đi xung quanh filter_C. Là filter_Ctùy chọn?
CurtisHx

@CurtisHx Bộ lọc C là bắt buộc. Có xin lỗi lỗi của tôi, tôi đã sao chép-dán.
Kevin Cittadini

2
Làm thế nào câu hỏi này có thể là bất khả tri ngôn ngữ ? Một giải pháp thành ngữ trong Java sẽ rất khác với một giải pháp thành ngữ trong Haskell. Bạn đã không quyết định một ngôn ngữ cho dự án của bạn?
200_success 6/03/2015

Câu trả lời:


20

Bạn chưa nói liệu các bộ lọc có bất kỳ tham số nào không. Ví dụ: filter_Acó thể là bộ lọc danh mục, do đó, đây không chỉ là câu hỏi "tôi có cần áp dụng không filter_A", mà có thể là "Tôi cần áp dụng filter_Avà trả lại tất cả các bản ghi trong trường danh mục = fooCategory".

Cách đơn giản nhất để thực hiện chính xác những gì bạn đã mô tả (nhưng đảm bảo đọc nửa sau của câu trả lời bên dưới) tương tự như các câu trả lời khác, nhưng tôi hoàn toàn không có bất kỳ kiểm tra boolean nào. Tôi sẽ xác định giao diện : FilterA, FilterB, FilterC. Sau đó, bạn có thể có một cái gì đó như (Tôi là một lập trình viên Java, vì vậy đây sẽ là cú pháp Java-esque):

class RequestFilters {
    FilterA filterA;
    FilterB filterB;
    FilterC filterC;
}

Sau đó, bạn có thể có một cái gì đó như thế này (sử dụng mẫu enum singleton từ Java hiệu quả ):

enum NoOpFilterA implements FilterA {
    INSTANCE;

    public List<Item> applyFilter(List<Item> input) {
       return input;
    }
}

Nhưng nếu bạn thực sự muốn một số mục được lọc, thay vào đó bạn có thể cung cấp một thể hiện của một FilterAtriển khai thực sự làm một cái gì đó. Phương pháp lọc của bạn sẽ rất đơn giản

List<Item> filterItems(List<Item> data, RequestFilters filters) {
    List<Item> returnedList = data;
    returnedList = filters.filterA.filter(data);
    returnedList = filters.filterB.filter(data);
    returnedList = filters.filterC.filter(data);
    return returnedList;
}

Nhưng tôi chỉ mới bắt đầu.

Tôi nghi ngờ rằng applyFiltercuộc gọi thực sự sẽ khá giống nhau cho cả ba loại bộ lọc. Nếu đó là trường hợp, tôi thậm chí sẽ không làm theo cách được mô tả ở trên. Bạn có thể nhận được mã sạch hơn bằng cách chỉ có một giao diện, sau đó thực hiện việc này:

class ChainedFilter implements Filter {
     List<Filter> filterList;

     void addFilter(Filter filter) {
          filterList.add(filter);
     }

     List<Item> applyFilter(List<Item> input) {
         List<Item> returnedList = input;
         for(Filter f : filterList) {
             returnedList = f.applyFilter(returnedList);
         }
         return returnedList;
     }
}

Sau đó, khi người dùng của bạn điều hướng qua các trang, bạn chỉ cần thêm một phiên bản mới của bất kỳ bộ lọc nào bạn cần khi thích hợp. Điều này sẽ cho phép bạn có thể áp dụng nhiều phiên bản của cùng một bộ lọc với các đối số khác nhau nếu bạn cần hành vi đó trong tương lai và cũng có thể thêm các bộ lọc bổ sung trong tương lai mà không phải thay đổi thiết kế của mình .

Ngoài ra, bạn có thể thêm một cái gì đó giống như NoOpFilterở trên hoặc bạn hoàn toàn không thể thêm một bộ lọc cụ thể vào danh sách, bất cứ điều gì dễ dàng hơn cho mã của bạn.


Cảm ơn bạn, vì bạn tìm thấy cách đơn giản nhất có thể để thay đổi logic mà không thay đổi mã là tốt. Điều này làm cho câu trả lời của bạn là tốt nhất. Tôi sẽ triển khai thiết kế mã này càng sớm càng tốt
Kevin Cittadini 6/03/2015

Nếu bạn có bạn Filternhư một Predicatesau đó bạn có thể sử dụng trực tiếp trong các StreamAPI. Nhiều ngôn ngữ có cấu trúc chức năng tương tự.
Boris the Spider

3
@BoristheSpider Điều đó chỉ khi anh ấy sử dụng Java 8; anh ta thậm chí không nói ngôn ngữ mình đang sử dụng. Các ngôn ngữ khác có cấu trúc như vậy nhưng tôi không muốn đi sâu vào tất cả các hương vị khác nhau về cách thực hiện điều đó
durron597

3
Hiểu - chỉ cần đề cập rằng đó là một con đường để khám phá nếu OP muốn cung cấp triển khai sạch nhất có thể. Bạn chắc chắn có +1 của tôi cho một câu trả lời đã xuất sắc.
Boris the Spider

3

Trong trường hợp này, điều quan trọng là phải tách logic của bộ lọc và luồng điều khiển về cách các bộ lọc chạy. Logic bộ lọc nên được tách ra thành các chức năng riêng lẻ, có thể chạy độc lập với nhau.

ApplyFilterA();
ApplyFilterB();
ApplyFilterC();

Trong mẫu mã được đăng, có 3 boolean filter_A, filter_Bfilter_C. Tuy nhiên, từ sơ đồ, filter_Cluôn luôn chạy, do đó có thể được thay đổi thành vô điều kiện.

LƯU Ý: Tôi giả sử rằng sơ đồ dòng điều khiển là chính xác. Có sự khác biệt giữa mã mẫu được đăng và sơ đồ luồng điều khiển.

Một đoạn mã riêng biệt kiểm soát bộ lọc nào được chạy

ApplyFilters(bool filter_A, bool filter_B)
{
    listOfProducts tmp;
    if (filter_A)
        ApplyFilterA();
    if (filter_B)
        ApplyFilterB();
    ApplyFilterC();
}

Có một sự tách biệt rõ ràng giữa việc kiểm soát bộ lọc nào chạy và bộ lọc làm gì. Phá vỡ hai mảnh logic đó.


+1 Điều này có vẻ đơn giản và tách rời hơn nhiều so với câu trả lời được chấp nhận.
nháy mắt

2

Tôi giả sử rằng bạn muốn thuật toán đơn giản nhất, rõ ràng nhất.
Trong trường hợp này, biết rằng bộ lọc c luôn được áp dụng, tôi sẽ loại bỏ logic if và áp dụng nó ở cuối bất kể. Vì nó nhìn trong sơ đồ của bạn, mỗi bộ lọc trước c, là tùy chọn, bởi vì mỗi bộ lọc có thể được áp dụng hoặc không. Trong trường hợp này, tôi sẽ sống ifs tách biệt với từng bộ lọc, mà không làm tổ và xâu chuỗi:

if filter_a
  do_filter_a()

if filter_b
  do_filter_b()

do_filter_c()

thay vào đó, nếu bạn có một sơ đồ với số lượng bộ lọc thay đổi, trước bộ lọc bắt buộc, thay vào đó, tôi sẽ lưu tất cả các bộ lọc vào một mảng, theo thứ tự chúng sẽ xuất hiện. Sau đó xử lý các bộ lọc tùy chọn trong vòng lặp và áp dụng bộ lọc bắt buộc ở cuối, bên ngoài vòng lặp:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)

for current_filter in optional_filters_array
  do_filter(current_filter)

do_required_filter()

hoặc là:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)
required_filter = last_filter


for current_filter in optional_filters_array
  do_filter(current_filter)

do_filter(required_filter)

của nguồn, bạn sẽ phải xác định chương trình con xử lý bộ lọc.


1

Tôi sẽ giả sử bộ lọcA, bộ lọcB và bộ lọcC thực sự sửa đổi danh sách các sản phẩm. Mặt khác, nếu chúng chỉ là kiểm tra if, thì có thể bỏ qua bộ lọcA và bộ lọcB vì tất cả các đường dẫn cuối cùng dẫn đến bộ lọcC. Mô tả của bạn về yêu cầu dường như ngụ ý rằng mỗi bộ lọc sẽ giảm danh sách sản phẩm.

Vì vậy, giả sử các bộ lọc thực sự làm giảm danh sách các sản phẩm, đây là một chút mã giả ...

class filter
    func check(item) returns boolean
endclass

func applyFilter(filter, productList) returns list
    newList is list
    foreach item in productList
        if filter.check(item) then
            add item to newList
        endif
    endfor 
    return newList
endfunc



filterA, filterB, filterC = subclasses of filter for each condition, chosen by the user
products = list of items to be filtered

if filterA then
    products = applyFilter(filterA, products)
endif

if filterB then
    products = applyFilter(filterB, products)
endif

if filterC then
    products = applyFilter(filterC, products)
endif

# use products...

Trong các yêu cầu của bạn, filterC không được tự động áp dụng, nhưng trong sơ đồ, nó là. Nếu yêu cầu là ít nhất nên áp dụng bộ lọcC cho dù thế nào đi chăng nữa, thì bạn sẽ gọi áp dụngFilter (bộ lọc, sản phẩm) mà không cần kiểm tra xem bộ lọc có được chọn không.

filterC = instance of filter, always chosen

...

# if filterC then
products = applyFilter(filterC, products)
# endif

0

Tôi tự hỏi nếu mô hình hóa các bộ lọc của bạn thành một số loại đối tượng trong biểu đồ sẽ có ý nghĩa. Ít nhất đó là những gì tôi nghĩ khi xem sơ đồ.

Nếu bạn mô hình hóa sự phụ thuộc của các bộ lọc như biểu đồ đối tượng, thì mã xử lý các đường dẫn luồng có thể sẽ khá đơn giản mà không có logic lông. Ngoài ra, biểu đồ (logic nghiệp vụ) có thể thay đổi, trong khi mã diễn giải biểu đồ vẫn giữ nguyên.

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.