วิธีหลีกเลี่ยงวิธีการมากไปที่มากไป?


16

เรามีสถานที่ค่อนข้างมากในซอร์สโค้ดของแอปพลิเคชันของเราที่คลาสหนึ่งมีวิธีการมากมายที่มีชื่อเดียวกันและพารามิเตอร์ที่แตกต่างกัน วิธีการเหล่านั้นมักจะมีพารามิเตอร์ทั้งหมดของวิธี 'ก่อนหน้า' บวกอีกหนึ่ง

มันเป็นผลมาจากวิวัฒนาการที่ยาวนาน (รหัสดั้งเดิม) และความคิดนี้ (ฉันเชื่อ):

" มีวิธี M ที่ทำสิ่ง A. ฉันต้องทำ A + B ตกลงฉันรู้ ... ฉันจะเพิ่มพารามิเตอร์ใหม่ให้กับ M สร้างวิธีการใหม่เพื่อย้ายรหัสจาก M ไปยังวิธีใหม่ ด้วยพารามิเตอร์อีกหนึ่งตัวให้ทำ A + B ตรงนั้นแล้วเรียกวิธีการใหม่จาก M ด้วยค่าเริ่มต้นของพารามิเตอร์ใหม่ "

นี่คือตัวอย่าง (ในภาษา Java-like-language):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

ฉันรู้สึกว่ามันผิด ไม่เพียง แต่เราไม่สามารถเพิ่มพารามิเตอร์ใหม่เช่นนี้ตลอดไปได้ แต่รหัสยากที่จะขยาย / เปลี่ยนแปลงเนื่องจากการพึ่งพาทั้งหมดระหว่างวิธีการ

ต่อไปนี้เป็นวิธีการทำได้ดีกว่า:

  1. แนะนำวัตถุพารามิเตอร์:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. ตั้งค่าพารามิเตอร์เป็นDocumentHomeวัตถุก่อนที่เราจะเรียกcreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. แยกงานออกเป็นวิธีต่างๆและเรียกพวกเขาตามต้องการ:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

คำถามของฉัน:

  1. ปัญหาที่อธิบายเป็นปัญหาจริงหรือไม่?
  2. คุณคิดอย่างไรเกี่ยวกับแนวทางแก้ไขที่แนะนำ คุณต้องการแบบไหน (ขึ้นอยู่กับประสบการณ์ของคุณ)
  3. คุณนึกถึงโซลูชันอื่น ๆ ได้ไหม?

1
คุณกำลังตั้งเป้าหมายภาษาอะไรหรือเป็นแค่ sth generell?
Knerd

ไม่มีภาษาใดภาษาหนึ่งโดยทั่วไป อย่าลังเลที่จะแสดงให้เห็นว่าคุณลักษณะในภาษาอื่นสามารถช่วยได้อย่างไร
Ytus

เหมือนที่ฉันบอกไว้ที่นี่programmers.stackexchange.com/questions/235096/… C # และ C ++ มีคุณสมบัติบางอย่าง
Knerd

ค่อนข้างชัดเจนว่าคำถามนี้ใช้กับทุกภาษาที่รองรับวิธีการดังกล่าวมากไป
Doc Brown

1
@DocBrown ตกลง แต่ไม่ใช่ทุกภาษารองรับตัวเลือกเดียวกัน;)
Knerd

คำตอบ:


20

อาจลองรูปแบบการสร้าง ? (หมายเหตุ: ผลการค้นหาของ Google ที่ค่อนข้างสุ่ม :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

ฉันไม่สามารถให้บทสรุปที่สมบูรณ์ว่าทำไมฉันถึงชอบผู้สร้างมากกว่าตัวเลือกที่คุณให้ แต่คุณระบุปัญหาใหญ่ด้วยรหัสจำนวนมาก หากคุณคิดว่าคุณต้องการพารามิเตอร์มากกว่าสองตัวสำหรับวิธีการหนึ่งคุณอาจมีโครงสร้างของโค้ดผิด (และบางคนอาจโต้แย้งว่าเป็นหนึ่ง!)

ปัญหาเกี่ยวกับวัตถุ params คือ (เว้นแต่ว่าวัตถุที่คุณสร้างเป็นจริงอย่างใด) คุณเพียงแค่ผลักปัญหาขึ้นในระดับและคุณจบลงด้วยกลุ่มของพารามิเตอร์ที่ไม่เกี่ยวข้องขึ้นรูป 'วัตถุ'

ความพยายามอื่น ๆ ของคุณดูเหมือนฉันเหมือนมีคนมาถึงรูปแบบของผู้สร้าง แต่ไม่ได้ไปถึงที่นั่น :)


ขอขอบคุณ. คำตอบของคุณสามารถเป็นคำตอบได้ 4 ใช่ ฉันกำลังมองหาคำตอบเพิ่มเติมในรูปแบบนี้: 'จากประสบการณ์ของฉันสิ่งนี้นำไปสู่ ​​... และสามารถแก้ไขได้ ... ' หรือ 'เมื่อฉันเห็นสิ่งนี้ในรหัสของเพื่อนร่วมงานของฉันฉันแนะนำให้เขา ... แทน'
Ytus

ฉันไม่รู้ ... ดูเหมือนว่าฉันกำลังจะย้ายปัญหา ตัวอย่างเช่นถ้าฉันสามารถสร้างเอกสารในส่วนต่าง ๆ ของแอปพลิเคชันได้ดีกว่าที่จะจัดระเบียบและทดสอบการแยกเอกสารนี้ในชั้นเรียนที่แยกกัน (เช่นใช้กDocumentoFactory) การมีbuilderสถานที่ต่าง ๆ เป็นการยากที่จะควบคุมการเปลี่ยนแปลงในอนาคตของการสร้างเอกสาร (เช่นเพิ่มฟิลด์บังคับใหม่ให้กับเอกสารตัวอย่าง) และเพิ่มรหัสสำเร็จรูปเพิ่มเติมในการทดสอบ
Dherik

1

การใช้อ็อบเจกต์พารามิเตอร์เป็นวิธีที่ดีในการหลีกเลี่ยงวิธีการมากไป (มากไป):

  • มันล้างรหัส
  • แยกข้อมูลจากฟังก์ชั่น
  • ทำให้รหัสบำรุงรักษามากขึ้น

อย่างไรก็ตามฉันจะไม่ไปไกลเกินไปกับมัน

มีน้ำหนักเกินที่นี่และไม่มีสิ่งเลวร้าย ได้รับการสนับสนุนโดยภาษาการเขียนโปรแกรมดังนั้นใช้เพื่อประโยชน์ของคุณ

ฉันไม่รู้รูปแบบของผู้สร้าง แต่ได้ใช้มัน "โดยบังเอิญ" ในบางโอกาส เช่นเดียวกับที่นี่: อย่าหักโหมจนเกินไป โค้ดในตัวอย่างของคุณจะได้รับประโยชน์จากมัน แต่การใช้เวลานานในการนำไปใช้กับทุกวิธีที่มีวิธีการโอเวอร์โหลดเดียวนั้นไม่มีประสิทธิภาพมากนัก

แค่ 2 เซ็นต์ของฉัน


0

ฉันสุจริตไม่เห็นปัญหาใหญ่กับรหัส ใน C # และ C ++ คุณสามารถใช้พารามิเตอร์ทางเลือกซึ่งจะเป็นทางเลือก แต่เท่าที่ฉันรู้ Java ไม่รองรับพารามิเตอร์ชนิดนั้น

ใน C # คุณสามารถทำให้โอเวอร์โหลดทั้งหมดเป็นส่วนตัวและวิธีหนึ่งที่มีพารามิเตอร์ทางเลือกนั้นเป็นแบบสาธารณะเพื่อเรียกสิ่งนั้น

เพื่อตอบคำถามของคุณตอนที่ 2 ฉันจะเอาวัตถุพารามิเตอร์หรือแม้แต่พจนานุกรม / HashMap

ชอบมาก

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

ตามข้อจำกัดความรับผิดชอบฉันเป็นโปรแกรมเมอร์ C # และ JavaScript คนแรกและเป็นโปรแกรมเมอร์ Java


4
มันเป็นทางออก แต่ฉันไม่คิดว่ามันเป็นทางออกที่ดี
Doc Brown

ถูกตัอง. เนื่องจากกรณีเช่นนั้นฉันชอบวิธีการโอเวอร์โหลดหรือพารามิเตอร์ทางเลือก
Knerd

2
การใช้พจนานุกรมเป็นพารามิเตอร์เป็นวิธีที่ง่ายในการลดจำนวนพารามิเตอร์ที่ชัดเจนให้กับวิธีการ แต่จะปิดบังการอ้างอิงที่แท้จริงของวิธีการ สิ่งนี้บังคับให้ผู้เรียกค้นหาสิ่งที่จำเป็นต้องมีในพจนานุกรมไม่ว่าจะเป็นการแสดงความคิดเห็นการเรียกไปยังเมธอดอื่นหรือการใช้เมธอดเอง
Mike Partridge

ใช้พจนานุกรมสำหรับพารามิเตอร์ทางเลือกเท่านั้นดังนั้นกรณีการใช้งานพื้นฐานไม่จำเป็นต้องอ่านเอกสารมากเกินไป
949300

0

ฉันคิดว่านี่เป็นตัวเลือกที่ดีสำหรับรูปแบบตัวสร้าง รูปแบบของตัวสร้างมีประโยชน์เมื่อคุณต้องการสร้างวัตถุประเภทเดียวกัน แต่มีรูปแบบต่างกัน

ในกรณีของคุณฉันจะสร้างด้วยวิธีการดังต่อไปนี้:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

จากนั้นคุณสามารถใช้ตัวสร้างด้วยวิธีต่อไปนี้:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

ในบันทึกด้านข้างฉันไม่รังเกียจการโอเวอร์โหลดง่าย ๆ สองสามอย่างอะไรคือสิ่งที่กรอบการทำงาน. NET ใช้พวกเขาไปทั่วทุกแห่งด้วยผู้ช่วย HTML อย่างไรก็ตามฉันจะประเมินสิ่งที่ฉันทำอีกครั้งหากฉันต้องผ่านพารามิเตอร์มากกว่าสองตัวไปยังทุกวิธี


0

ฉันคิดว่าbuilderวิธีการแก้ปัญหาสามารถทำงานได้ในสถานการณ์ส่วนใหญ่ แต่ในกรณีที่ซับซ้อนมากขึ้นผู้สร้างของคุณจะมีความซับซ้อนในการตั้งค่าเพราะมันง่ายที่จะยอมรับข้อผิดพลาดบางอย่างในลำดับของวิธีการวิธีการที่จะต้องเปิดเผย ฯลฯ ดังนั้นพวกเราหลายคนจะชอบทางออกที่ง่ายที่สุด

หากคุณเพียงสร้างตัวสร้างอย่างง่ายเพื่อสร้างเอกสารและกระจายรหัสนี้ในส่วนต่างๆ (คลาส) ของแอปพลิเคชันมันจะยากที่จะ:

  • จัดระเบียบ : คุณจะมีหลายชั้นเรียนสร้างเอกสารด้วยวิธีที่แตกต่างกัน
  • บำรุงรักษา : การเปลี่ยนแปลงใด ๆ เกี่ยวกับการสร้างอินสแตนซ์เอกสาร (เช่นเพิ่มการบังคับใหม่ที่ยื่น) จะนำคุณไปสู่การผ่าตัด Shotgun
  • ทดสอบ : ถ้าคุณกำลังทดสอบ classe ที่สร้างเอกสารคุณจะต้องเพิ่มรหัสสำเร็จรูปเพื่อตอบสนองการเริ่มต้นเอกสาร

แต่นี่ไม่ได้ตอบคำถาม OP

ทางเลือกในการโอเวอร์โหลด

ทางเลือกบางอย่าง:

  • เปลี่ยนชื่อวิธีการ : ถ้าชื่อวิธีเดียวกันคือการสร้างความสับสนให้ลองเปลี่ยนชื่อวิธีการที่จะสร้างชื่อที่มีความหมายที่ดีกว่าcreateDocumentเช่น: createLicenseDriveDocument, createDocumentWithOptionalFieldsฯลฯ ของหลักสูตรนี้สามารถนำคุณไปสู่ชื่อวิธียักษ์ดังนั้นนี้ไม่ได้ ทางออกสำหรับทุกกรณี
  • ใช้วิธีการแบบคงที่ วิธีการนี้คล้ายกันหากเปรียบเทียบกับทางเลือกแรกด้านบน คุณสามารถใช้ชื่อที่มีความหมายกับแต่ละกรณีและยกตัวอย่างเอกสารจากวิธีการที่คงที่เช่น:DocumentDocument.createLicenseDriveDocument()
  • สร้างติดต่อกัน : คุณสามารถสร้างวิธีเดียวที่เรียกว่าcreateDocument(InterfaceDocument interfaceDocument)และสร้างที่แตกต่างกันการใช้งานInterfaceDocumentสำหรับ ตัวอย่างเช่น: createDocument(new DocumentMinPagesCount("name")). แน่นอนว่าคุณไม่จำเป็นต้องมีการใช้งานเพียงครั้งเดียวสำหรับแต่ละกรณีเนื่องจากคุณสามารถสร้างคอนสตรัคเตอร์ได้มากกว่าหนึ่งรายการในการติดตั้งแต่ละครั้งการจัดกลุ่มบางฟิลด์ที่เหมาะสมกับการใช้งานนั้น รูปแบบนี้จะเรียกว่าการก่อสร้างเหลื่อม

หรือเพียงอยู่กับการแก้ปัญหาเกินพิกัด แม้จะเป็นบางครั้งวิธีการแก้ปัญหาที่น่าเกลียดมีข้อบกพร่องไม่มากในการใช้มัน ในกรณีนั้นฉันต้องการใช้วิธีการโอเวอร์โหลดในคลาสที่แยกออกมาเช่นเดียวกับDocumentoFactoryที่สามารถถูกฉีดเป็นการพึ่งพาในคลาสที่ต้องการสร้างเอกสาร ฉันสามารถจัดระเบียบและตรวจสอบความถูกต้องของเขตข้อมูลได้โดยไม่ต้องสร้างตัวสร้างที่ดีและรักษารหัสไว้ในที่เดียว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.