PDA

View Full Version : سوال: استفاده از کانستراکتور برای ایجاد یک شئ در یک کلاس



Sina.iRoid
یک شنبه 18 آبان 1393, 16:57 عصر
سلام. اگر امکانش هست به کد زیر نگاه کنید:

کد کلاس TextFieldFrame:


import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPasswordField;
import javax.swing.JTextField;


public class TextFieldFrame extends JFrame{

private final JTextField textField1; //text field with set size
private final JPasswordField passwordField; //password field with text

//Constructor
public TextFieldFrame() {
super("Window Demo");
setLayout(new FlowLayout());

//constructed textfield with 10 columns
textField1 = new JTextField(10);
add(textField1);

//constructed passwordfield with default text
passwordField = new JPasswordField("Hidden text");
add(passwordField);

//register event handler
TextFieldHandler handler = new TextFieldHandler();
textField1.addActionListener(handler);
passwordField.addActionListener(handler);
}

//getter
public JTextField getTextField1(){
return textField1;
}
public JPasswordField getPasswordField(){
return passwordField;
}
}


و کد کلاس TextFieldHandler:


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;


public class TextFieldHandler implements ActionListener{

//object from TextFieldFrame
TextFieldFrame tff = new TextFieldFrame();

@Override
public void actionPerformed(ActionEvent e) {

String string;

if (e.getSource() == tff.getTextField1()) {
string = String.format("textfield1: %s", e.getActionCommand());
}else if (e.getSource() == tff.getTextField2()) {
string = String.format("textfield2: %s", e.getActionCommand());
}else if (e.getSource() == tff.getTextField3()) {
string = String.format("textfield3: %s", e.getActionCommand());
}else{
string = String.format("passwordfield: %s", e.getActionCommand());
}

//display JTextField Content
JOptionPane.showMessageDialog(null, string);
}
}


و کد کلاس اصلی:

import javax.swing.JFrame;


public class DriverClass {


public static void main(String[] args) {

TextFieldFrame frame = new TextFieldFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOS E);
frame.setSize(350, 100);
frame.setVisible(true);
}
}


حالا مشکل اینجاست که اگر الان برنامه رو اجرام کنیم، برنامه کرش می کنه!
اما اگر در کلاس TextFieldHandler با استفاده از کانستراکتور یک شی ایجاد می کنیم برنامه درست عمل می کنه. یعنی اگر کد کلاس TextFieldHandler و به صورت زیر بنویسیم:


//object from TextFieldFrame
TextFieldFrame tff;

//Constructor
public TextFieldHandler(TextFieldFrame tff) {
this.tff = tff;
}


و در کلاس TextFieldFrame موقع ایجاد یک شی به صورت زیر بنویسیم:

TextFieldHandler handler = new TextFieldHandler(this);

اگر اینطور بنویسیم برنامه درست اجرا میشه. اما من دلیلشو نمی دونم.
اگر راهنماییم کنید ممنون میشم.

محمد فدوی
یک شنبه 18 آبان 1393, 17:31 عصر
سلام. یه نکته رو فک میکنم شما هنوز متوجه نشدی در مورد اشیاء. شما هر دفعه که یه شیء رو new میکنی، دوباره ساخته میشه و دوتا شیء از یه کلاس با همدیگه برابر نیستن:


TextFieldHandler h1 = new TextFieldHandler();
TextFieldHandler h2 = new TextFieldHandler();


الان h1 و h2 با اینکه کاملا مشابه همدیگه هستن ولی توی حافظه دو نقطه‌ی مجزا رو اشغال کردن و اصلا به هم مربوط نیستن. الان شما وقتی توی خط ۹ از کلاس TextFieldHandler یه نمونه از کلاس TextFieldFrame میسازی، این شیء اونی نیست که شما توی متد main ساختیش نیست و بافرض اینکه برنامه‌ت دوباره توی Infinity Loop کذایی نیفته بازم نتیجه‌ی دلخواهت رو نمیده چون شما یه فریم داری که داری نمایشش میدی و TextFieldهات روش قرار داره که میخوای هندلشون کنی و یه فریم بی استفاده که توی شیء TextFieldHandler ساختی و اصلا نمایش داده نشده!

از این گذشته اگه به روش اولت بنویسی بازم تو همون حلقه‌ی بی‌نهایت میفته برنامت که قبلا هم در موردش با هم حرف زدیم. دوباره بیا مثل دفعات قبل خودمونو بذاریم جای کامپیوتر! تو متد main یه دونه TextFieldFrame ساختیم. خب برای ساختن این شیء باید تو خط ۲۶ از همین کلاس یدونه TextFieldHandler بسازیم و برای ساخت این شیء باید تو خط ۹ کلاس TextFieldHandler یه دونه TextFieldFrame بسازیم!! می‌بینی؟ بازم شد همون حلقه‌ی بی‌نهایت.

روش دومی که به کار بردی برای برنامه‌ت درست تره و جوابگوئه. اما اگه به من میگفتن اینو بنویس خود کلاس TextFieldFrame رو طوری مینوشتم که واسط ActionListener رو پیاده‌سازی کنه و احتمالا اینجوری مینوشتمش:

public class TextFieldFrame extends JFrame implements ActionListener {

private final JTextField textField1,
textField2,
textField3; //text field with set size
private final JPasswordField passwordField; //password field with text

//Constructor
public TextFieldFrame() {
super("Window Demo");
setLayout(new FlowLayout());

//constructed textfield with 10 columns
textField1 = new JTextField(10);
add(textField1);


textField2 = new JTextField(10);
add(textField2);

textField3 = new JTextField(10);
add(textField3);

//constructed passwordfield with default text
passwordField = new JPasswordField("Hidden text");
add(passwordField);

// I'm event handler, too!
textField1.addActionListener(this);
textField2.addActionListener(this);
textField3.addActionListener(this);
passwordField.addActionListener(this);
}


@Override
public void actionPerformed(ActionEvent e) {
Component source = e.getSource();
String string;

if (source == textField1) {
string = String.format("textfield1: %s", e.getActionCommand());
}else if (source == textField2) {
string = String.format("textfield2: %s", e.getActionCommand());
}else if (source == textField3) {
string = String.format("textfield3: %s", e.getActionCommand());
}else {
string = String.format("passwordfield: %s", e.getActionCommand());
}

// display Field Content
JOptionPane.showMessageDialog(null, string);
}
}

البته یادت رفته بود textField2 و textField3 رو بسازی و من اضافشون کردم. اینجوری به نظر من کدت ساده‌تر هم میشه. البته نظر خودت مهمتره.

نکته انحرافی:
همیشه سعی کن برای چک کردن تساوی دوتا شیء به جای اپراتور == از متد equals استفاده کنی:


if(textField1.equals(source)) {
...
}

خصوصا وقتی میخوای دوتا String رو چک کنی. در مورد دلیلش باید جداگونه حرف بزنیم اما همینقدر بدون که خیلی وقتا تفاوتی ایجاد نمیشه ولی بعضی اوقات اگه از == استفاده کنی نتایج عجیبی میگیری!

Sina.iRoid
یک شنبه 18 آبان 1393, 17:52 عصر
سلام.
ابتدا سپاس از شما برای راهنمایی های بی نظریتون.

در مورد سوالی که تو یه پست دیگه برای تغییر نام فایل پرسیده بودم، هنوز کامل نتونستم که اون برنامه رو بنویسم. برای همین دارم از مثال های ساده تر استفاده می کنم که بتونم اونو بنویسم. (البته اونم سادست :) ).
در مورد این برنامه، این یه مثال از کتاب دایتل هست. و این کتاب هم دقیقا همینطور که شما گفتین نوشته. البته فرقش اینه که از یه کلاس داخلی استفاده کرده. دلیل اینکه من یه کلاس جدا تعریف کردم این بود که نمی خوام کدها خیلی با هم قاطی بشه. (البته نمی دونم که این کارم درست هست یا نه!؟) اما فک می کنم که اگر اینطور بنویسم بهتره.
درضمن اگر در این مورد هم یه پاسخ کوتاه بدین ممنون میشم. من یه جایی خوندم که استفاده از inner class ها زیاده کاره درستی در شی گرایی نیست!؟ همانطور که گفتم کتاب دایتل از inner class استفاده کرده.

باز هم سپاس از شما :)

محمد فدوی
یک شنبه 18 آبان 1393, 18:00 عصر
خواهش میکنم. خوشحالم از پشت کاری که شما داری.
بحث در این مورد مفصله. آره استفاده از کلاس‌های داخلی (خصوصا کلاس‌های داخلی غیر static) یه سری پیچیدگی‌ها رو اضافه میکنه و حتی یه سری کارا بصورت هوشمند باید توسط کامپایلر انجام شه.
اما اینکه بگیم بده استفاده ازشون (به طور مطلق) خیلی حرف متعصبانه‌ایه و اینطور اظهار نظر کردن به نظر زیاد حرفه‌ای نیست. من به شخصه کمتر از کلاس‌های درونی استفاده کردم... ولی بعضی اوقات هم به نظر بد نیست استفاده ازشون. اینو (http://stackoverflow.com/questions/1028850/are-inner-classes-commonly-used-in-java-are-they-bad) بخون.
در هر صورت در مورد مثال شما به نظر نمیاد استفاده از کلاس داخلی خیلی واجب باشه.

dasssnj
دوشنبه 19 آبان 1393, 04:26 صبح
اما من به دو دلیل با Anonymous inner class ها موافقم .
اول به خاطر امنیت ، چون مثلا اگه توی خط 500 کلاست یه ActionListener داشته باشی . پیدا کردن این ActinListener مورد نظر از بین بقیه برای کرک کننده سخته . به نظر من مشکلی که Proguard داره اینه که کلاس داخلی ها را به کلاس جدا تبدیل می کنه و کار کرک را یکمی راحت می کنه . (البته شاید توی تنظیماتش بشه غیر قعالش کرد)

دوم به خاطر سرعت کد نویسیه . چون ساختن یه کلاس و نوشتن کد در اون وقت گیره .