เป็นวิธีปฏิบัติที่ดีในการหลีกเลี่ยงค่าคงที่โดยใช้ getters หรือไม่


25

มันเป็นการดีหรือไม่ที่จะแทนที่ค่าคงที่ที่ใช้นอกคลาสโดย getters

ตัวอย่างเช่นใช้ดีกว่าif User.getRole().getCode() == Role.CODE_ADMINหรือif User.getRole().isCodeAdmin()?

นั่นจะนำไปสู่คลาสนี้:

class Role {
    constant CODE_ADMIN = "admin"
    constant CODE_USER = "user"

    private code

    getRoleCode() {
       return Role.code
    }

    isCodeAdmin () {
       return Role.code == Role.CODE_ADMIN
    }

    isCodeUser () {
       return Role.code == Role.CODE_USER
    }
}

15
User.HasRole(Role.Admin)ฉันอยากจะใช้สิ่งที่ต้องการ
CodesInChaos


4
ตรวจสอบหลักการบอกอย่าถามหลักการ
Andy

ฉันถามสถานที่ตั้ง: User.getRole().getCode()เป็นการอ่านที่ไม่พึงประสงค์อยู่แล้วการเปรียบเทียบจรรยาบรรณกับบทบาททำให้ไม่เป็นความจริงมากขึ้น
msw

คำตอบ:


47

ปิดครั้งแรกโปรดทราบว่าทำอะไรเช่นentity.underlyingEntity.underlyingEntity.method()นี้ถือว่าเป็นกลิ่นรหัสตามกฎหมายของ Demeter ด้วยวิธีนี้คุณจะเปิดเผยรายละเอียดการใช้งานจำนวนมากแก่ผู้บริโภค และความต้องการในการต่อเติมหรือดัดแปลงแต่ละอย่างของระบบจะส่งผลเสียอย่างมาก

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


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

เปรียบเทียบ

public bool HasRole(string role)

กับ

public enum Role { Admin, User }

public bool HasRole(Role role)

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


ต่อไปคือการตัดสินใจว่าบทบาทจะมีลักษณะอย่างไร คุณสามารถใช้ enum ที่จัดเก็บโดยตรงกับผู้ใช้:

public enum Role
{
    Admin,
    User
}

public class User
{
    private Role _role;

    public bool HasRole(Role role)
    {
        return _role == role;
    }

    // or
    public bool IsAdmin()
    {
        return _role == Role.Admin;
    }
}

ในทางกลับกันถ้าคุณต้องการให้บทบาทของคุณมีพฤติกรรมตัวเองมันควรจะซ่อนรายละเอียดของการตัดสินใจประเภทของมันอีกครั้ง:

public enum RoleType
{
    User,
    Admin
}

public class Role
{
    private RoleType _roleType;

    public bool IsAdmin()
    {
        return _roleType == RoleType.Admin;
    }

    public bool IsUser()
    {
        return _roleType == RoleType.User;
    }

    // more role-specific logic...
}

public class User
{
    private Role _role;

    public bool IsAdmin()
    {
        return _role.IsAdmin();
    }

    public bool IsUser()
    {
        return _role.IsUser();
    }
}

อย่างไรก็ตามนี่เป็นคำพูดที่ค่อนข้างละเอียดและความซับซ้อนจะเพิ่มขึ้นตามการเพิ่มบทบาทแต่ละครั้ง - ซึ่งมักจะเป็นวิธีที่รหัสสิ้นสุดเมื่อคุณพยายามปฏิบัติตามกฎหมายของ Demeter อย่างเต็มที่ คุณควรปรับปรุงการออกแบบโดยยึดตามข้อกำหนดที่เป็นรูปธรรมของระบบที่มีการจำลอง

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


10
ฉันหวังว่านี่เป็นวิธีที่ผู้คนจะทำทุกที่ มีทะเยอทะยานในแอตทริบิวต์ privatr เป็นอย่างอื่นเพียงเพื่อตรวจสอบว่ามันจะเท่ากับสิ่งอื่นเป็นเช่นการปฏิบัติที่น่ากลัว
Andy

2
Re "instance.property.property.method () ... " นั่นไม่ใช่สิ่งที่เหลวไหลใช่ไหม
Peter Mortensen

2
@PeterMortensen มันแตกต่างจากส่วนต่อประสานที่คล่องแคล่ว อินเตอร์เฟซได้อย่างคล่องแคล่วกับชนิดแน่นอนจะช่วยให้คุณสามารถที่จะทำให้สายการเรียกฟังก์ชั่นเช่นX X.foo().bar().fee()...ในอินเตอร์เฟซได้อย่างคล่องแคล่วนี้ foo บาร์และค่าธรรมเนียมทั้งหมดจะเป็นฟังก์ชั่นภายในชั้นกลับวัตถุของการพิมพ์X Xแต่สำหรับ 'instance.property.property.method () ที่กล่าวถึงในตัวอย่างนี้การpropertyเรียกสองสายจะอยู่ในคลาสที่แยกกัน ปัญหาที่เกิดขึ้นคือคุณกำลังผ่านสิ่งที่เป็นนามธรรมหลายเลเยอร์เพื่อรับรายละเอียดระดับต่ำ
Shaz

10
คำตอบที่ดี แต่กฏหมายของ Demeter ไม่ใช่แบบฝึกหัดการนับแต้ม instance.property.property.method()ไม่จำเป็นต้องเป็นการละเมิดหรือแม้แต่กลิ่นรหัส ขึ้นอยู่กับว่าคุณกำลังทำงานกับวัตถุหรือโครงสร้างข้อมูล Node.Parent.RightChild.Remove()อาจไม่ใช่การละเมิด LoD (แม้ว่าส่งกลิ่นเนื่องจากเหตุผลอื่น) var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;เป็นการละเมิดแม้ว่าฉันจะใช้จุดเดียวเสมอ
Carl Leth

1
ฉันเข้าใจว่าinstance.property.property.method()เป็นการละเมิด LoD แต่ OP มีinstance.method().method()ที่ควรปรับ ในตัวอย่างล่าสุดของคุณมีรหัสเพื่อสำเร็จรูปมากที่ทำหน้าที่เป็นซุ้มสำหรับเท่านั้นUser Role
Bergi

9

ดูเหมือนว่าบ้าคลั่งที่มีฟังก์ชั่นเพื่อตรวจสอบว่ารหัสที่เก็บไว้เป็นรหัสผู้ดูแลระบบ สิ่งที่คุณอยากรู้คือบุคคลนั้นเป็นผู้ดูแลหรือไม่ ดังนั้นหากคุณไม่ต้องการแสดงค่าคงที่คุณก็ไม่ควรเปิดเผยว่ามีรหัสและเรียกใช้เมธอด isAdmin () และ isUser ()

ที่กล่าวว่า "ถ้า User.getRole (). getCode () == Role.CODE_ADMIN" จริงๆแล้วมีเพียงไม่กี่คนที่ตรวจสอบว่าผู้ใช้นั้นเป็นผู้ดูแลระบบ ผู้พัฒนาต้องจำไว้ว่าต้องเขียนบรรทัดนั้นกี่เรื่อง เขาหรือเธอต้องจำไว้ว่าผู้ใช้มีบทบาทบทบาทมีรหัสและคลาสบทบาทมีค่าคงที่สำหรับรหัส นั่นเป็นข้อมูลจำนวนมากที่เกี่ยวกับการใช้งาน


3
มันแย่กว่านั้น: ผู้ใช้มีบทบาทเดียวเสมอไม่มากหรือน้อย
Deduplicator

5

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

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

ไม่เพียง แต่สั้นและสะอาด แต่ยังใช้งานและเข้าใจได้ง่าย และ - ที่สำคัญที่สุดคือ - ยากที่จะเข้าใจผิด


2

ในขณะที่ฉันส่วนใหญ่เห็นด้วยกับคำแนะนำเพื่อหลีกเลี่ยงค่าคงที่และมีวิธีการisFoo()อื่น ๆ ตัวอย่างหนึ่งที่เป็นไปได้

หากมีค่าคงที่หลายร้อยเหล่านี้และการโทรใช้น้อยอาจไม่คุ้มค่ากับความพยายามในการเขียนวิธีการหลายร้อยของ isConstant1, isConstant2 โดยเฉพาะอย่างยิ่งในกรณีนี้การใช้ค่าคงที่นั้นสมเหตุสมผล

โปรดทราบว่าการใช้ enums หรือhasRole()หลีกเลี่ยงความจำเป็นในการเขียนวิธีการนับร้อยจึงเป็นวิธีที่ดีที่สุดในโลกที่เป็นไปได้ทั้งหมด


2

ฉันไม่คิดว่าตัวเลือกอย่างใดอย่างหนึ่งที่คุณนำเสนอนั้นผิดปกติ

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

if (user.getRole().equals("Administrator")) ...

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

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

public bool isActuary() { return code==Role.ACTUARY; }
public bool isAccountant() { return code==Role.ACTUARY; }
... etc ...

โดยส่วนตัวแล้วฉันจะหลีกเลี่ยงสายการโทร ฉันอยากจะเขียน

if (user.getRole().equals(Role.FOOBATER))

แล้วก็

if (user.getRole().getRoleCode()==Role.FOOBATER_CODE)

และ ณ จุดนั้นเหตุใดจึงเขียนบันทึก:

if (user.hasRole(Role.FOOBATER))

จากนั้นให้ชั้นผู้ใช้ทราบวิธีตรวจสอบบทบาท

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