การบรรทุกเกินพิกัดเป็นตัวอย่างของหลักการเปิด / ปิดหรือไม่


12

Wikipedia พูดว่า

"เอนทิตีของซอฟต์แวร์ (คลาส, โมดูล, ฟังก์ชั่น, ฯลฯ ) ควรเปิดเพื่อขยาย แต่ปิดเพื่อแก้ไข"

ฟังก์ชั่นคำดึงดูดสายตาของฉันและตอนนี้ฉันสงสัยว่าเราสามารถสันนิษฐานได้ว่าการสร้างเกินพิกัดสำหรับวิธีการสามารถถือได้ว่าเป็นตัวอย่างของหลักการเปิด / ปิดหรือไม่?

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

bool IsAdmin(userId)

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

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

ด้วยวิธีนี้เราได้ขยายฟังก์ชั่นของเราผ่านการสร้างโอเวอร์โหลด (ฟังก์ชั่นควรจะเปิดเพื่อขยาย)

มันเป็นตัวอย่างหลักการเปิด / ปิดหรือไม่?

คำตอบ:


5

ฉันจะตีความคำสั่ง wiki เป็นการส่วนตัวดังนี้:

  • สำหรับชั้นเรียน: รู้สึกอิสระที่จะสืบทอดชั้นเรียนและแทนที่หรือ ขยายฟังก์ชั่นของมัน แต่อย่าแฮกชั้นเรียนดั้งเดิมจึงเปลี่ยนสิ่งที่มันทำ
  • สำหรับโมดูล (อาจเป็นห้องสมุดเป็นต้น): อย่าลังเลที่จะเขียนโมดูล / ไลบรารีใหม่ที่ล้อมรอบต้นฉบับผสานฟังก์ชันเข้ากับรุ่นที่ใช้ง่ายขึ้นหรือขยายต้นฉบับด้วยคุณสมบัติพิเศษ แต่ไม่เปลี่ยนโมดูลดั้งเดิม
  • สำหรับฟังก์ชั่น (เช่นฟังก์ชั่นคงที่ไม่ใช่วิธีการเรียน): ตัวอย่างของคุณมีเหตุผลสำหรับฉัน; ใช้ฟังก์ชั่น IsAdmin เดิม (int) อีกครั้งภายใน IsAdmin ใหม่ (สตริง) ต้นฉบับจะไม่เปลี่ยนแปลง func ใหม่จะขยายฟังก์ชันการทำงานของมัน

อย่างไรก็ตามการยิงด้วยเท้าคือหากรหัสของคุณใช้คลาส 'cUserInfo' ซึ่งมี IsAdmin (int) อยู่คุณจะทำผิดกฎและเปลี่ยนคลาส กฎจะถูกเก็บไว้เศร้าถ้าคุณทำคลาสใหม่ cUserWithNameInfo: คลาสาธารณะ cUserInfo และวาง IsAdmin (สตริง) ของคุณแทนที่ที่นั่น ถ้าฉันเป็นเจ้าของรหัสฐานฉันจะไม่เชื่อฟังกฎนี้ ฉันจะบอก bollocks และเพียงแค่ทำการเปลี่ยนแปลงที่คุณแนะนำ


3

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

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

คุณควรขยายคลาสนั้นจริงๆ

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

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

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

ดังที่ลุงบ๊อบพูด (เน้นการเน้นของฉัน):

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


ขอบคุณสำหรับคำอธิบายที่ดีของคุณ แต่การมี 1,000 รอบไปกลับหรือรอบเดียวไม่ใช่ประเด็นหลักที่นี่ ดังนั้นเราจะลืมเกี่ยวกับการแสดงในขณะนี้ อย่างไรก็ตามสิ่งที่ฉันทำคือการขยายชั้นเรียนเพราะฉันได้เพิ่มวิธีการอื่นลงไปด้วยชื่อเดียวกัน แต่มีพารามิเตอร์อินพุตที่แตกต่างกัน แต่ฉันชื่นชมคำพูดของคุณจากลุงบ๊อบจริงๆ +1 ;)
Saeed Neamati

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

2

โมดูลที่สอดคล้องกับหลักการเปิดมีสองคุณสมบัติหลัก

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

  2. คุณไม่ได้ทำการเปลี่ยนแปลงใด ๆ กับวิธีการที่มีอยู่ดังนั้นคุณจะไม่ละเมิดกฎข้อที่สอง

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

Ref: http://www.objectmentor.com/resources/articles/ocp.pdf


1

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

BigContrivedMethod(int1, int2, string1)แต่ขอบอกว่าคุณได้ฟังก์ชั่นที่เรียกว่า BigContrivedMethodทำสามสิ่ง: สิ่งที่ 1, สิ่งที่ 2 และสิ่งที่ 3 ณ จุดนี้การใช้ BCM ซ้ำอาจเป็นเรื่องยากเพราะทำมากเกินไป Refactoring มัน (ถ้าเป็นไปได้) ลงContrivedFunction1(int), ContrivedFunction2(int)และContrivedFunction3(string)ช่วยให้คุณสามขนาดเล็กวิธีการที่มุ่งเน้นเพิ่มเติมที่คุณสามารถรวมได้ง่ายขึ้น

และนั่นคือกุญแจสู่ OCP เกี่ยวกับวิธีการ / ฟังก์ชั่น: การจัดวาง คุณ "ขยาย" ฟังก์ชั่นโดยการเรียกพวกเขาจากฟังก์ชั่นอื่น ๆ

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


ฉันคิดว่าคุณพูดถึงหลักการความรับผิดชอบเดี่ยวที่นี่ไม่ใช่ OCP
Saeed Neamati

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

คำตอบนี้ควรได้รับการสนับสนุนมากขึ้น
Ashish Gupta

0

คุณกำลังปฏิบัติตามหลักการแบบเปิดหากคุณกำลังเปลี่ยนแปลงพฤติกรรมของโปรแกรมโดยการเขียนรหัสใหม่แทนที่จะเปลี่ยนรหัสเดิม

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

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

ดังนั้นวิธีนี้ใช้กับกรณีของคุณ:

หากคุณกำลังเพิ่มฟังก์ชั่นใหม่ในชั้นเรียนของคุณคลาสของคุณจะไม่เปิด / ปิด แต่ลูกค้าของฟังก์ชั่นที่คุณทำงานหนักเกินไปนั้น

หากคุณเพียงแค่เพิ่มฟังก์ชั่นใหม่ที่ทำงานในชั้นเรียนของคุณแล้วทั้งฟังก์ชั่นและลูกค้าของฟังก์ชั่นของคุณจะเปิด / ปิด

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