อาร์กิวเมนต์หลายตัวในการเรียกใช้ฟังก์ชันเทียบกับอาร์เรย์เดียว


24

ฉันมีฟังก์ชั่นที่ใช้ในชุดของพารามิเตอร์จากนั้นนำไปใช้กับพวกเขาเป็นเงื่อนไขในแบบสอบถาม SQL อย่างไรก็ตามในขณะที่ฉันชอบอาร์เรย์อาร์กิวเมนต์เดียวที่มีเงื่อนไขตัวเอง:

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        switch ($param) {
            case 'name':
                $query->where('name', $value);
                break;
            case 'phone':
                $query->join('phone');
                $query->where('phone', $value);
                break;
        }
    }
}

เพื่อนร่วมงานของฉันต้องการแสดงรายการอาร์กิวเมนต์ทั้งหมดแทนอย่างชัดเจน:

function searchQuery($name = '', $phone = '') {
    if ($name) {
        $query->where('name', $value);
    }

    if ($phone) {
        $query->join('phone');
        $query->where('phone', $value);
    }
}

การโต้เถียงของเขาคือการระบุข้อโต้แย้งอย่างชัดเจนพฤติกรรมของฟังก์ชั่นจะชัดเจนมากขึ้นเมื่อเทียบกับการต้องเจาะลึกรหัสเพื่อค้นหาว่าข้อโต้แย้งลึกลับ$paramคืออะไร

ปัญหาของฉันคือการได้รับ verbose มากเมื่อจัดการกับข้อโต้แย้งมากมายเช่น 10+ มีวิธีปฏิบัติที่ต้องการหรือไม่? สถานการณ์กรณีที่เลวร้ายที่สุดของฉันจะเห็นบางสิ่งดังต่อไปนี้:

searchQuery('', '', '', '', '', '', '', '', '', '', '', '', 'search_query')


1
หากฟังก์ชั่นนั้นต้องการคีย์เฉพาะเป็นพารามิเตอร์อย่างน้อยคีย์เหล่านั้นควรได้รับการบันทึกไว้ใน DocBlock ซึ่งเป็นวิธีที่ IDE สามารถแสดงข้อมูลที่เกี่ยวข้องได้โดยไม่ต้องเจาะเข้าไปในโค้ด en.wikipedia.org/wiki/PHPDoc
Ilari Kajaste

2
เคล็ดลับประสิทธิภาพ: foreachไม่จำเป็นในกรณีนี้คุณสามารถใช้if(!empty($params['name']))แทนและforeach switch
chiborg

1
ตอนนี้คุณมีวิธีหนึ่งที่คุณใช้ ฉันขอแนะนำให้ดูที่นี่: book.cakephp.org/2.0/en/models/…สำหรับการสร้างวิธีการเพิ่มเติม พวกเขายังสามารถสร้างขึ้นอย่างน่าอัศจรรย์สำหรับการค้นหามาตรฐานและพัฒนาในแบบกำหนดเองสำหรับการค้นหาที่เฉพาะเจาะจง โดยทั่วไปแล้วจะทำให้ API ที่ชัดเจนสำหรับผู้ใช้ของรูปแบบ
Luc Franken


2
หมายเหตุเกี่ยวกับ 'เคล็ดลับประสิทธิภาพ' ด้านบน: อย่าใช้!empty($params['name'])เพื่อทดสอบพารามิเตอร์ - ตัวอย่างเช่นสตริง "0" จะว่างเปล่า มันเป็นเรื่องดีที่จะใช้array_key_existsในการตรวจสอบที่สำคัญหรือถ้าคุณไม่สนใจเกี่ยวกับisset null
AmadeusDrZaius

คำตอบ:


27

IMHO เพื่อนร่วมงานของคุณถูกต้องสำหรับตัวอย่างข้างต้น การตั้งค่าของคุณอาจสั้น แต่ก็ยังอ่านง่ายและบำรุงรักษาน้อย ถามคำถามว่าทำไมการเขียนฟังก์ชั่นในตอนแรกฟังก์ชั่นของคุณ 'มาที่โต๊ะ' - ฉันต้องเข้าใจสิ่งที่มันทำและวิธีการมันในรายละเอียดที่ดีเพียงใช้ ด้วยตัวอย่างของเขาแม้ว่าฉันไม่ใช่โปรแกรมเมอร์ PHP แต่ฉันสามารถเห็นรายละเอียดเพียงพอในการประกาศฟังก์ชั่นที่ฉันไม่ต้องกังวลกับการใช้งาน

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


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

2
@xiankai ในตัวอย่างนั้นฉันอาจจะสร้างหนึ่งอาร์เรย์พารามิเตอร์สำหรับwhereอาร์กิวเมนต์หนึ่งสำหรับjoinspecifiers ฯลฯ โดยการตั้งชื่อพวกเขาอย่างเหมาะสมที่จะยังคงเอกสารด้วยตนเอง
Jan Doggen

ถ้าฉันใช้ setter / getter แทนและฉันไม่ผ่านการโต้แย้งเลย มันเป็นการปฏิบัติที่ไม่ดี? มันไม่ใช่จุดประสงค์ของการใช้ setter / getter หรือไม่?
lyhong

ฉันจะท้าทายว่าการตั้งค่าของ OP คือ "อ่านได้น้อยลง" (อย่างไร?) และบำรุงรักษาน้อยกว่า searchQuery ('', '', '', 'foo', '', '', '', 'บาร์') นั้นอ่านได้น้อยกว่าหรือบำรุงรักษาได้ดีกว่า searchQuery (['q' => 'foo', 'x' => 'bar']) การโต้แย้งจำนวนมากไม่จำเป็นว่าจะต้องเป็นรหัสกลิ่นก็ได้เช่นกัน ตัวอย่างเช่นแบบสอบถาม () และถึงแม้จะมีจำนวนอาร์กิวเมนต์น้อยลงการขาดความมั่นคงในลำดับอาร์กิวเมนต์ที่เกิดขึ้นเมื่อมีการส่งผ่านอาร์กิวเมนต์โดยตรงแสดงให้เห็นถึงความคิดที่ไม่ดีสำหรับพารามิเตอร์ฮาร์ดโค้ด เพียงแค่ดูที่ฟังก์ชั่นสตริงและอาเรย์ใน PHP สำหรับความไม่สอดคล้องกันดังกล่าว
MikeSchinkel

4

คำตอบของฉันคือไม่เชื่อเรื่องภาษามากหรือน้อย

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

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

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


1

ในกรณีของคุณฉันต้องการวิธีการของเพื่อนร่วมงานของคุณ หากคุณกำลังเขียนโมเดลและฉันใช้โมเดลของคุณเพื่อพัฒนาโมเดลเหล่านั้น ฉันเห็นลายเซ็นต์ของวิธีการของเพื่อนร่วมงานของคุณและสามารถใช้งานได้ทันที

ในขณะที่ฉันจะต้องผ่านการใช้งานsearchQueryฟังก์ชั่นของคุณเพื่อดูพารามิเตอร์ที่ฟังก์ชั่นของคุณคาดหวัง

ฉันต้องการแนวทางของคุณเฉพาะในกรณีที่searchQueryถูก จำกัด ให้ค้นหาเฉพาะในตารางเดียวดังนั้นจะไม่มีการเข้าร่วม ในกรณีนี้ฟังก์ชั่นของฉันจะเป็นดังนี้:

function searchQuery($params = array()) {
    foreach($params as $param => $value) {
        $query->where($param, $value);
    }
} 

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


1

ทำทั้งสองอย่าง array_mergeอนุญาตให้มีรายการที่ชัดเจนที่ด้านบนของฟังก์ชั่นตามที่เพื่อนร่วมงานของคุณชอบในขณะที่รักษาพารามิเตอร์จากการเทอะทะตามที่คุณต้องการ

ฉันขอแนะนำอย่างยิ่งให้ใช้คำแนะนำของ @ chiborg จากความคิดเห็นของคำถาม - ชัดเจนกว่าที่คุณต้องการ

function searchQuery($params = array()) {
    $defaults = array(
        'name' => '',
        'phone' => '',
        ....
    );
    $params = array_merge($defaults, $params);

    if(!empty($params['name'])) {
        $query->where('name', $params['name']);
    }
    if (!empty($params['phone'])) {
        $query->join('phone');
        $query->where('phone', $params['phone']);
    }
    ....
}

0

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

/**
 * Executes a search in the DB with the constraints specified in the $queryString
 * @var $queryString string The search parameters in a query string format (ie
 *      "foo=abc&bar=hello"
 * @return ResultSet the result set of performing the query
 */
function searchQuery($queryString) {
  $params = parse_str($queryString);
  if (isset($params['name'])) {
    $query->where('name', $params['name']);
  }
  if (isset($params['phone'])) {
    $query->join('phone');
    $query->where('phone', $params['phone']);
  }
  ...

  return ...;
}

และเรียกว่าชอบ

$result = searchQuery('name=foo&phone=555-123-456');

คุณสามารถใช้http_build_queryในการแปลงจากอาเรย์แบบสัมพันธ์เป็นสตริง (กลับด้านparse_str)

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