ใช้วิธีสร้างหรือตั้งค่า?


16

ฉันกำลังทำงานกับรหัส UI ที่ฉันมีActionชั้นเรียนเช่นนี้ -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

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

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

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

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

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


1
ภาษาที่คุณใช้ไม่อนุญาตให้มีตัวสร้างมากเกินไปใช่ไหม
Mat

1
ฉันกำลังใช้ Java.So ใช่มันอนุญาตและฉันคิดว่านั่นอาจเป็นวิธีหนึ่งในการจัดการกับปัญหานี้ได้
zswap

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

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

คำตอบ:


15

ประโยชน์เดียวที่ฉันเห็นด้วยรหัสต้นฉบับคือผู้ใช้สามารถสร้าง Action class โดยไม่ต้องคิดมากเกี่ยวกับการตั้งค่าข้อความ

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

ฉันเห็นสองเส้นทางที่ชัดเจนไปข้างหน้า:

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

10

การสร้างมากไปจะเป็นวิธีที่ง่ายและตรงไปตรงมาที่นี่:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

มันดีกว่าการโทรใน.setTextภายหลังเพราะวิธีนี้ไม่จำเป็นต้องเขียนทับactionTextสามารถเป็นสิ่งที่ตั้งใจไว้ตั้งแต่เริ่มต้น

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


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

3
สำหรับคุณสมบัติที่ 2, 3, .. คุณสามารถใช้เทคนิคเดียวกันได้ แต่ยิ่งคุณต้องการปรับแต่งมากเท่าไหร่คุณสมบัตินี้ก็จะยิ่งมากขึ้นเท่านั้น ในบางจุดมันจะสมเหตุสมผลมากกว่าที่จะใช้รูปแบบโรงงาน / ผู้สร้างเช่นกัน @ michael-kjorling กล่าวในคำตอบของเขา
Janos

6

เพิ่มวิธีการ 'setText' อย่างคล่องแคล่ว:

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

อะไรที่ชัดเจนกว่านั้น หากคุณตัดสินใจที่จะเพิ่มคุณสมบัติที่กำหนดเองได้อื่นก็ไม่มีปัญหา


+1, ฉันเห็นด้วยและเพิ่มคำตอบอื่นซึ่งประกอบกับคุณสมบัติเพิ่มเติม ฉันคิดว่า API ที่คล่องแคล่วชัดเจนกว่าที่จะเข้าใจเมื่อคุณมีตัวอย่างคุณสมบัติมากกว่า 1 รายการ
Machado

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

1

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

มันจะทำให้รหัสของคุณอ่านได้มากขึ้นและจากมุมมองของฉันง่ายมากขึ้นและAham "sexy" เพื่อเขียน

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

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

และการใช้งานจะเป็นเช่นนี้:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");

ความคิดที่ไม่ดีจะเกิดอะไรขึ้นถ้าพวกเขาลืมที่จะตั้งค่าข้อความหรืออะไรก็ตามที่สำคัญ
Prakhar

1

คำแนะนำในการใช้ตัวสร้างหรือผู้สร้างนั้นเป็นเรื่องปกติ แต่ในประสบการณ์ของฉันคิดถึงประเด็นสำคัญสำหรับการกระทำ

  1. อาจจำเป็นต้องเป็นสากล
  2. มีแนวโน้มที่การตลาดจะเปลี่ยนแปลงในนาทีสุดท้าย

ฉันขอแนะนำอย่างยิ่งว่าควรจะอ่านชื่อเครื่องมือคำแนะนำไอคอน ฯลฯ ... จากไฟล์คุณสมบัติ XML เป็นต้นตัวอย่างเช่นสำหรับการดำเนินการ File-Open คุณสามารถส่งผ่านคุณสมบัติได้และจะมองหา

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

นี่เป็นรูปแบบที่ง่ายต่อการแปลเป็นภาษาฝรั่งเศสลองไอคอนใหม่ที่ดีกว่า ฯลฯ โดยไม่ต้องเสียเวลาโปรแกรมเมอร์หรือคอมไพล์ใหม่

นี่เป็นเพียงคร่าวๆคร่าวๆเหลืออยู่มากสำหรับผู้อ่าน ... มองหาตัวอย่างอื่นของการทำให้เป็นสากล


0

มันไม่มีประโยชน์ที่จะเรียก setText (actionText) หรือ setTooltip ("My tool tool tip") ในตัวสร้าง มันง่ายกว่า (และคุณจะได้รับประสิทธิภาพมากขึ้น) หากคุณเพียงกำหนดค่าเริ่มต้นให้กับฟิลด์ที่เกี่ยวข้องโดยตรง:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

หากคุณเปลี่ยน actionText ในช่วงเวลาชีวิตของวัตถุที่สอดคล้องกันของ MyAction คุณควรวางเมธอด setter ถ้าไม่ได้เริ่มต้นฟิลด์ในตัวสร้างโดยไม่ต้องมีวิธีการตั้งค่า

เนื่องจากคำแนะนำเครื่องมือและรูปภาพเป็นค่าคงที่ให้ถือว่าเป็นค่าคงที่ มีสาขา:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

ที่จริงแล้วเมื่อออกแบบวัตถุทั่วไป (ไม่ใช่ถั่วหรือวัตถุที่แสดงถึงโครงสร้างข้อมูลอย่างเคร่งครัด) เป็นความคิดที่ไม่ดีที่จะให้ setters และ getters เนื่องจากเป็นชนิดของ encapsulation break


4
คอมไพเลอร์ที่มีความสามารถครึ่งทางใด ๆ และ JITter ควรอินไลน์การเรียก setText () ฯลฯ ดังนั้นความแตกต่างของประสิทธิภาพระหว่างการเรียกใช้ฟังก์ชันเพื่อทำการมอบหมายและการมอบหมายนั้นแทนการเรียกใช้ฟังก์ชัน กว่าไม่เป็นศูนย์
CVn

0

ฉันคิดว่านี่เป็นเรื่องจริงถ้าเราจะสร้างคลาสแอคชั่นทั่วไป (เช่นอัปเดตซึ่งใช้เพื่ออัปเดตพนักงานแผนก ... ) ทุกอย่างขึ้นอยู่กับสถานการณ์ หากมีการกระทำที่เฉพาะเจาะจง (เช่นอัปเดตพนักงาน) คลาส (ใช้สถานที่หลายแห่งในแอปพลิเคชัน - อัปเดตพนักงาน) จะถูกสร้างขึ้นด้วยความตั้งใจที่จะเก็บข้อความคำแนะนำและภาพเหมือนกันทุกที่ในแอปพลิเคชัน ดังนั้นการเข้ารหัสสามารถทำได้สำหรับข้อความคำแนะนำเครื่องมือและรูปภาพเพื่อจัดเตรียมข้อความคำแนะนำเครื่องมือและรูปภาพเริ่มต้น ยังคงให้ความยืดหยุ่นมากขึ้นในการปรับแต่งเหล่านี้ควรมีวิธีการตั้งค่าที่สอดคล้องกัน เก็บไว้ในใจเพียง 10% ที่เราต้องเปลี่ยน การทำ text action ทุกครั้งจากผู้ใช้สามารถทำให้ข้อความต่างกันทุกครั้งสำหรับการกระทำเดียวกัน เช่น 'Update Emp', 'Update Employee', 'Change Employee' หรือ 'Edit Employee'


ฉันคิดว่า Constructor ที่ทำงานมากเกินไปควรจะแก้ปัญหาได้ เพราะในทุกกรณี "10%" คุณจะสร้าง Action ด้วยข้อความเริ่มต้นก่อนแล้วจึงเปลี่ยนข้อความการกระทำโดยใช้เมธอด "setText ()" ทำไมไม่ตั้งค่าข้อความที่เหมาะสมในขณะที่สร้างการกระทำ
zswap

0

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

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

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

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


0

ในโซลูชันที่นำเสนอต่อไปนี้ superclass เป็นนามธรรมและมีสมาชิกทั้งสามตั้งเป็นค่าเริ่มต้น

คลาสย่อยมีคอนสตรัคเตอร์ที่แตกต่างกันดังนั้นโปรแกรมเมอร์สามารถสร้างอินสแตนซ์ได้

ถ้ามีใช้ตัวสร้างแรกสมาชิกทั้งหมดจะมีค่าเริ่มต้น

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

หากใช้ตัวสร้างที่สามคุณจะสร้างอินสแตนซ์ด้วยค่าใหม่สำหรับ actionText และ toolTip โดยปล่อยให้ imageURl ซึ่งมีค่าเริ่มต้น ...

และอื่น ๆ

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


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