ข้อดีข้อเสียของค่าคงที่ของอินเทอร์เฟซ [ปิด]


105

อินเทอร์เฟซ PHP อนุญาตให้กำหนดค่าคงที่ในอินเทอร์เฟซเช่น

interface FooBar
{
    const FOO = 1;
    const BAR = 2;
}
echo FooBar::FOO; // 1

คลาสการใช้งานใด ๆ จะมีค่าคงที่เหล่านี้โดยอัตโนมัติเช่น

class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1

สิ่งนี้ของฉันเองคือสิ่งที่ Global is Evilอะไรที่ทั่วโลกเป็นความชั่วร้ายแต่ฉันสงสัยว่าสิ่งเดียวกันนี้ใช้กับค่าคงที่ของอินเทอร์เฟซหรือไม่ เนื่องจากการเข้ารหัสกับอินเทอร์เฟซถือเป็นแนวทางปฏิบัติที่ดีโดยทั่วไปการใช้ค่าคงที่ของอินเตอร์เฟสเป็นค่าคงที่เพียงอย่างเดียวที่ยอมรับให้ใช้นอกบริบทของคลาสได้หรือไม่

ในขณะที่ฉันอยากทราบความคิดเห็นส่วนตัวของคุณและคุณใช้ค่าคงที่ของอินเทอร์เฟซหรือไม่ แต่ฉันก็มองหาเหตุผลที่เป็นประโยชน์ในคำตอบของคุณเป็นหลัก ฉันไม่ต้องการให้เป็นคำถามประเภทการสำรวจความคิดเห็น ฉันสนใจว่าค่าคงที่ของอินเทอร์เฟซมีผลต่อการบำรุงรักษาอย่างไร การมีเพศสัมพันธ์ หรือ Unit Testing. เกี่ยวข้องอย่างไรกับ SOLID PHP อย่างไร? มันละเมิดหลักการเข้ารหัสใด ๆ ที่ถือเป็นแนวปฏิบัติที่ดีใน PHP หรือไม่? คุณจะเข้าใจ ...

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


1
อืมฉันไม่เคยเจอความจำเป็นในการกำหนดค่าคงที่ในอินเทอร์เฟซมาก่อน เป็นเรื่องน่ารู้ว่าคลาสที่ใช้อินเทอร์เฟซไม่สามารถแทนที่ค่าคงที่ได้ในขณะที่คลาสที่ขยายซึ่งกันและกันเท่านั้นที่สามารถแทนที่ค่าคงที่ได้
Charles

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

คำตอบ:


135

ดีฉันคิดว่ามันเดือดลงไปความแตกต่างระหว่างดีและดีพอ

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

แย่:

interface User {
    const TYPE_ADMINISTRATOR = 1;
    const TYPE_USER          = 2;
    const TYPE_GUEST         = 3;
}

ดีพอแล้ว:

interface HTTPRequest_1_1 {
    const TYPE_CONNECT = 'connect';
    const TYPE_DELETE  = 'delete';
    const TYPE_GET     = 'get';
    const TYPE_HEAD    = 'head';
    const TYPE_OPTIONS = 'options';
    const TYPE_POST    = 'post';
    const TYPE_PUT     = 'put';

    public function getType();
}

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

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

หากคุณเขียนแสดงMyClass::FOOว่าคุณกำหนดรายละเอียดการใช้งานของMyClassไฟล์. สิ่งนี้จะสร้างการเชื่อมต่อแบบแข็งซึ่งทำให้โค้ดของคุณมีความยืดหยุ่นน้อยลงดังนั้นจึงควรหลีกเลี่ยง อย่างไรก็ตามอินเทอร์เฟซมีอยู่เพื่ออนุญาตการมีเพศสัมพันธ์ประเภทนี้ ดังนั้นจึงMyInterface::FOOไม่แนะนำการเชื่อมต่อคอนกรีตใด ๆ ด้วยเหตุนี้ฉันจะไม่แนะนำอินเทอร์เฟซเพียงเพื่อเพิ่มค่าคงที่ให้กับมัน

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

อีกครั้งนั่นเป็นเพียงมุมมองของฉันที่มีต่อมัน ...


4
คุณจะแนะนำอะไรเป็นรูปแบบอื่นสำหรับผู้ใช้ในกรณีนี้
Jacob

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

3
คำตอบที่ชัดเจนและชัดเจนมาก! +1
Decent Dabbler

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

2
@FrederikKrautwald: คุณสามารถหลีกเลี่ยงเงื่อนไขที่มีความหลากหลาย (ในกรณีส่วนใหญ่): ตรวจสอบคำตอบนี้และดูการพูดคุยเกี่ยวกับ Clean Code นี้ ...
ircmaxell

10

ฉันคิดว่าโดยปกติแล้วมันจะดีกว่าที่จะจัดการกับค่าคงที่ค่าคงที่แจกแจงเป็นพิเศษเป็นประเภทแยกต่างหาก ("คลาส") จากอินเทอร์เฟซของคุณ:

define(TYPE_CONNECT, 'connect');
define(TYPE_DELETE , 'delete');
define(TYPE_GET    , 'get');
define(TYPE_HEAD   , 'head');
define(TYPE_OPTIONS, 'options');
define(TYPE_POST   , 'post');
define(TYPE_PUT    , 'put');

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

หรือถ้าคุณต้องการใช้คลาสเป็นเนมสเปซ:

class TypeHTTP_Enums
{
  const TYPE_CONNECT = 'connect';
  const TYPE_DELETE  = 'delete';
  const TYPE_GET     = 'get';
  const TYPE_HEAD    = 'head';
  const TYPE_OPTIONS = 'options';
  const TYPE_POST    = 'post';
  const TYPE_PUT     = 'put';
}

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

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

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