ทำไมมันไม่กลายเป็นรูปแบบทั่วไปในการใช้ตัวตั้งค่าในตัวสร้าง


23

อุปกรณ์เสริมและตัวดัดแปลง (aka setters and getters) มีประโยชน์ด้วยเหตุผลสามประการ:

  1. พวกเขา จำกัด การเข้าถึงตัวแปร
    • ตัวอย่างเช่นตัวแปรสามารถเข้าถึงได้ แต่ไม่สามารถแก้ไขได้
  2. พวกเขาตรวจสอบพารามิเตอร์
  3. พวกเขาอาจทำให้เกิดผลข้างเคียง

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

public class Cat {
    private int age;

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

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

/**
 * Sets the age for the current cat
 * @param age an integer with the valid values between 0 and 25
 * @return true if value has been assigned and false if the parameter is invalid
 */
public boolean setAge(int age) {
    //Validate your parameters, valid age for a cat is between 0 and 25 years
    if(age > 0 && age < 25) {
        this.age = age;
        return true;
    }
    return false;
}

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

public class Cat {
    private int age;

    public Cat(int age) {
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }

    /**
     * Sets the age for the current cat
     * @param age an integer with the valid values between 0 and 25
     * @return true if value has been assigned and false if the parameter is invalid
     */
    public boolean setAge(int age) {
        //Validate your parameters, valid age for a cat is between 0 and 25 years
        if(age > 0 && age < 25) {
            this.age = age;
            return true;
        }
        return false;
    }

}

แต่ใครจะคิดว่าวิธีที่สองนี้ปลอดภัยกว่ามาก:

public class Cat {
    private int age;

    public Cat(int age) {
        //Use the modifier instead of assigning the value directly.
        setAge(age);
    }

    public int getAge() {
        return this.age;
    }

    /**
     * Sets the age for the current cat
     * @param age an integer with the valid values between 0 and 25
     * @return true if value has been assigned and false if the parameter is invalid
     */
    public boolean setAge(int age) {
        //Validate your parameters, valid age for a cat is between 0 and 25 years
        if(age > 0 && age < 25) {
            this.age = age;
            return true;
        }
        return false;
    }

}

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


1
@stg ขอบคุณจุดที่น่าสนใจ! คุณสามารถขยายมันและใส่คำตอบลงในตัวอย่างของสิ่งเลวร้ายที่อาจเกิดขึ้นได้หรือไม่?
Vlad Spreys


8
@stg ที่จริงแล้วมันเป็นรูปแบบการต่อต้านที่จะใช้วิธีการ overridable ในนวกรรมิกและเครื่องมือคุณภาพของรหัสจำนวนมากจะชี้ให้เห็นว่าเป็นข้อผิดพลาด คุณไม่รู้ว่าเวอร์ชั่นที่ถูกแทนที่จะทำอะไรและมันอาจทำสิ่งที่น่ารังเกียจเช่นปล่อยให้ "สิ่งนี้" หลบหนีไปก่อนที่ตัวสร้างจะเสร็จสิ้นซึ่งสามารถนำไปสู่ปัญหาแปลก ๆ
Michał Kosmulski

1
@gnat: ฉันไม่เชื่อว่านี่เป็นสิ่งที่ซ้ำกันของวิธีการในชั้นเรียนจะเรียก getters และ setters ของตัวเอง? เนื่องจากลักษณะของการเรียก constructor ที่ถูกเรียกบนวัตถุที่ไม่ได้เกิดขึ้นอย่างสมบูรณ์
Greg Burghardt

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

คำตอบ:


37

การใช้เหตุผลเชิงปรัชญาทั่วไปมาก

โดยทั่วไปแล้วเราขอให้ผู้สร้างให้ (เป็นเงื่อนไขหลัง) รับประกันบางอย่างเกี่ยวกับสถานะของวัตถุที่สร้างขึ้น

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

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

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

ตัวอย่างที่เฉพาะเจาะจง

  1. ตัวอย่างของคุณเองเกี่ยวกับวิธีที่คุณคิดว่าสิ่งนี้ควรมีลักษณะผิดปกติ:

    public Cat(int age) {
        //Use the modifier instead of assigning the value directly.
        setAge(age);
    }

    setAgeนี้ไม่ได้ตรวจสอบค่าที่ส่งกลับจาก เห็นได้ชัดว่าการเรียกตัวตั้งค่านั้นไม่รับประกันความถูกต้องหลังจากทั้งหมด

  2. ข้อผิดพลาดง่าย ๆ เช่นขึ้นอยู่กับการเริ่มต้นเช่น:

    class Cat {
      private Logger m_log;
      private int m_age;
    
      public void setAge(int age) {
        // FIXME temporary debug logging
        m_log.write("=== DEBUG: setting age ===");
        m_age = age;
      }
    
      public Cat(int age, Logger log) {
        setAge(age);
        m_log = log;
      }
    };

    ที่การบันทึกชั่วคราวของฉันทำลายทุกสิ่ง อ๊ะ!

นอกจากนี้ยังมีภาษาเช่น C ++ ที่เรียกตัวตั้งค่าจากตัวสร้างหมายถึงการเริ่มต้นที่สูญเปล่า (ซึ่งสำหรับตัวแปรสมาชิกบางตัวอย่างน้อยก็ควรหลีกเลี่ยง)

ข้อเสนอง่ายๆ

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

class Cat {
  private int m_age;
  private static void checkAge(int age) {
    if (age > 25) throw CatTooOldError(age);
  }

  public void setAge(int age) {
    checkAge(age);
    m_age = age;
  }

  public Cat(int age) {
    checkAge(age);
    m_age = age;
  }
};

หรือดียิ่งขึ้นถ้าเป็นไปได้: เข้ารหัสข้อ จำกัด ในประเภทคุณสมบัติและให้มันตรวจสอบความถูกต้องของตัวเองในการมอบหมาย:

class Cat {
  private Constrained<int, 25> m_age;

  public void setAge(int age) {
    m_age = age;
  }

  public Cat(int age) {
    m_age = age;
  }
};

และในที่สุดเพื่อความสมบูรณ์ครบถ้วนเสื้อคลุมตรวจสอบตัวเองใน C ++ โปรดทราบว่าแม้ว่ามันจะยังทำการตรวจสอบที่น่าเบื่อ แต่คลาสนี้ไม่ได้ทำสิ่งใดเลยมันค่อนข้างง่ายต่อการตรวจสอบ

template <typename T, T MAX, T MIN=T{}>
class Constrained {
    T val_;
    static T validate(T v) {
        if (MIN <= v && v <= MAX) return v;
        throw std::runtime_error("oops");
    }
public:
    Constrained() : val_(MIN) {}
    explicit Constrained(T v) : val_(validate(v)) {}

    Constrained& operator= (T v) { val_ = validate(v); return *this; }
    operator T() { return val_; }
};

ตกลงมันไม่สมบูรณ์จริง ๆ ฉันได้คัดลอกและย้ายตัวสร้างและการกำหนดต่างๆ


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

ดังนั้นคุณจะพิจารณาการใช้วิธีการตั้งค่าในการปฏิบัติที่ไม่ดีคอนสตรัคถ้ามี 1) ไม่มีตรรกะใน setters หรือ 2) ตรรกะใน setters เป็นอิสระจากรัฐ? ฉันไม่ได้ทำบ่อย แต่ฉันเองได้ใช้วิธีการตั้งค่าในคอนสตรัคเตอร์ด้วยเหตุผลหลัง (เมื่อมีเงื่อนไขบางอย่างในเซ็ตเตอร์ที่ต้องนำไปใช้กับตัวแปรสมาชิกเสมอ
Chris Maggiulli

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

13

เมื่อวัตถุถูกสร้างขึ้นมันเป็นคำนิยามที่ไม่ได้เกิดขึ้นอย่างเต็มที่ พิจารณาวิธีการตั้งค่าที่คุณให้ไว้:

public boolean setAge(int age) {
    //Validate your parameters, valid age for a cat is between 0 and 25 years
    if(age > 0 && age < 25) {
        this.age = age;
        return true;
    }
    return false;
}

จะเกิดอะไรขึ้นถ้าส่วนหนึ่งของการตรวจสอบความถูกต้องในการsetAge()รวมการตรวจสอบกับageคุณสมบัติเพื่อให้แน่ใจว่าวัตถุสามารถเพิ่มอายุเท่านั้น? สภาพนั้นอาจดูเหมือน:

if (age > this.age && age < 25) {
    //...

ดูเหมือนจะเป็นการเปลี่ยนแปลงที่ไร้เดียงสาเนื่องจากแมวสามารถเคลื่อนที่ผ่านเวลาไปในทิศทางเดียวเท่านั้น ปัญหาเดียวคือคุณไม่สามารถใช้เพื่อตั้งค่าเริ่มต้นthis.ageเพราะถือว่าthis.ageมีค่าที่ถูกต้องอยู่แล้ว

การหลีกเลี่ยง setters ใน constructors ทำให้มันชัดเจนว่าสิ่งเดียวที่ constructor กำลังทำคือการตั้งค่าตัวแปรอินสแตนซ์และข้ามพฤติกรรมอื่น ๆ ที่เกิดขึ้นใน setter


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

ไม่มีปัญหาเช่นเดียวกับใน Java / C # ทุกเขตข้อมูลจะเป็นศูนย์ก่อนที่ตัวสร้าง - การภาวนา
Deduplicator

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

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

1
คำตอบนี้คือ IMHO ไม่มีจุด "เมื่อวัตถุถูกสร้างขึ้นมันเป็นไปตามคำนิยามที่ไม่ได้เกิดขึ้นอย่างสมบูรณ์" - ในช่วงกลางของกระบวนการก่อสร้างแน่นอน แต่เมื่อผู้สร้างเสร็จสิ้นข้อ จำกัด ที่ตั้งใจไว้ใด ๆ ที่มีต่อแอตทริบิวต์จะต้องมีไว้ ฉันจะเรียกว่าข้อบกพร่องการออกแบบที่รุนแรง
Doc Brown

6

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

  1. หากมีการตรวจสอบข้อ จำกัด ในตัวตั้งค่ามันจะไม่อยู่ในตัวสร้างของคลาสเพื่อให้แน่ใจว่าจะไม่สามารถข้ามได้หรือไม่

  2. ทำไมไม่เรียกตัวตั้งค่าโดยตรงเพื่อจุดประสงค์นี้จากตัวสร้าง

คำตอบของคำถามแรกนั้นชัดเจนว่า "ใช่" ตัวสร้างเมื่อมันไม่ทิ้งข้อยกเว้นควรปล่อยให้วัตถุอยู่ในสถานะที่ถูกต้องและถ้าค่าบางอย่างถูกห้ามสำหรับคุณลักษณะบางอย่างแล้วมันไม่มีเหตุผลที่จะปล่อยให้ ctor หลีกเลี่ยงสิ่งนี้

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


ฉันยังไม่เข้าใจว่าทำไมการแยกลอจิกจาก setter เพื่อให้ตัวสร้างสามารถใช้งานได้ดีกว่า ... วิธีนี้เป็นจริงที่แตกต่างกันมากกว่าเพียงแค่โทร setters ในการสั่งซื้อที่เหมาะสม ?? โดยเฉพาะอย่างยิ่งการพิจารณาว่าถ้าตัวตั้งค่าของคุณง่ายอย่างที่ควร "เป็น" เพียงส่วนเดียวของตัวตั้งค่าที่ตัวสร้างของคุณ ณ จุดนี้ไม่ได้เรียกใช้คือการตั้งค่าที่แท้จริงของสนามต้นแบบ ...
svidgen

2
@svidgen: ดูตัวอย่าง JimmyJames: ในระยะสั้นมันไม่ควรใช้วิธี overridable ใน ctor ที่จะทำให้เกิดปัญหา คุณสามารถใช้ setter ใน ctor ถ้ามันเป็นขั้นสุดท้ายและไม่ได้เรียกวิธีการ overridable อื่น ๆ ยิ่งกว่านั้นการแยกการตรวจสอบจากวิธีการจัดการเมื่อมันล้มเหลวจะช่วยให้คุณมีโอกาสจัดการมันแตกต่างกันใน ctor และใน setter
Doc Brown

ตอนนี้ฉันมั่นใจน้อยลงแล้ว! แต่ขอบคุณสำหรับการชี้ฉันไปที่คำตอบอื่น ๆ ...
svidgen

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

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

2

ต่อไปนี้เป็นโค้ด Java ที่โง่เง่าที่แสดงให้เห็นถึงปัญหาที่คุณสามารถพบได้โดยใช้วิธีการที่ไม่ใช่ขั้นสุดท้ายในตัวสร้าง

import java.util.regex.Pattern;

public class Problem
{
  public static final void main(String... args)
  {
    Problem problem = new Problem("John Smith");
    Problem next = new NextProblem("John Smith", " ");

    System.out.println(problem.getName());
    System.out.println(next.getName());
  }

  private String name;

  Problem(String name)
  {
    setName(name);
  }

  void setName(String name)
  {
    this.name = name;
  }

  String getName()
  {
    return name;
  }
}

class NextProblem extends Problem
{
  private String firstName;
  private String lastName;
  private final String delimiter;

  NextProblem(String name, String delimiter)
  {
    super(name);

    this.delimiter = delimiter;
  }

  void setName(String name)
  {
    String[] parts = name.split(Pattern.quote(delimiter));

    this.firstName = parts[0];
    this.lastName = parts[1];
  }

  String getName()
  {
    return firstName + " " + lastName;
  }
}

เมื่อฉันรันสิ่งนี้ฉันจะได้ NPE ในตัวสร้าง NextProblem นี่เป็นตัวอย่างเล็กน้อยของหลักสูตร แต่สิ่งต่าง ๆ อาจซับซ้อนได้อย่างรวดเร็วหากคุณมีการสืบทอดหลายระดับ

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

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


นี่เป็นปัญหาของการใช้ setters ใน Constructor อย่างไรแทนที่จะเป็นปัญหาของการสืบทอดที่ดำเนินการไม่ดี ??? กล่าวอีกนัยหนึ่งถ้าคุณตรวจสอบการสร้างฐานไม่ถูกต้องทำไมคุณถึงเรียกมันว่า!
svidgen

1
ฉันคิดว่าประเด็นคือการเอาชนะ setter ไม่ควรทำให้ตัวสร้างไม่ถูกต้อง
Chris Wohlert

@ChrisWohlert ตกลง ... แต่ถ้าคุณเปลี่ยนตรรกะตัวตั้งค่าของคุณเป็นความขัดแย้งกับตรรกะตัวสร้างของคุณมันเป็นปัญหาโดยไม่คำนึงว่าตัวสร้างของคุณใช้ตัวตั้งค่า ไม่ว่าเวลาก่อสร้างจะล้มเหลวในภายหลังหรือล้มเหลวอย่างเห็นไม่ชัด (b / c คุณข้ามกฎธุรกิจของคุณ)
svidgen

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

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

1

มันขึ้นอยู่กับ!

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

อย่างไรก็ตามหากคุณต้องการดำเนินการที่ซับซ้อนเพื่อจัดเรียงวัตถุก่อน setter (หรือสอง!) สามารถดำเนินการได้สำเร็จอย่าใช้ setter

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


ฉันจะเพิ่มด้วยหากความจำของฉันจากหลังสุดนั้นมีความแม่นยำจากระยะไกลนี่เป็นเหตุผลที่ฉันถูกสอนในวิทยาลัยด้วย และมันไม่ได้อยู่ในแนวเดียวกันกับโครงการที่ประสบความสำเร็จฉันมีความสามารถพิเศษในการทำงาน

ถ้าฉันต้องเดาฉันพลาดบันทึก "clean code" ณ จุดที่ระบุข้อผิดพลาดทั่วไปบางอย่างที่สามารถเกิดขึ้นจากการใช้ setters ใน Constructor แต่สำหรับส่วนของฉันฉันไม่ได้ตกเป็นเหยื่อของข้อบกพร่องใด ๆ เช่น ...

ดังนั้นฉันขอยืนยันว่าการตัดสินใจครั้งนี้ไม่จำเป็นที่จะต้องกวาดล้างและไร้เหตุผล

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