PDA

View Full Version : یک برنامه ساده برای نظر خواهی



sia_2007
شنبه 28 آذر 1388, 21:12 عصر
سلام خدمت دوستان عزیز
من یک تکه کد نوشته ام.
با ذکر چند تا نکته میخواستم آنرا تحت نظرخواهی شما دوستان عزیز قرار دهم.

یک :
برنامه به هیچ وجه کامل نشده ؛ و کامل شده آن تا یک هفته دیگر آپلود میشود.

دو :
بخش ADO.Net آن به شدت جای کار دارد ؛ تا جایی که احتمال دارد آنرا با ابزاری دیگر بنویسم.

سوم :
این یک برنامه چند لایه نیست؛ بلکه نظر طراحی خودم است.
ولو بسیار شبیه آن باشد ؛ یا نباشد.

چهارم این که از انتقادات سازنده یا غیر سازنده ؛ تند یا کند ، به شدت استقبال میشود.

پنجم :
بدین وسیله هر گونه ادعا در برنامه نویسی Net Framework. را تکذیب میکنم.

و در انتها :
کد مال خودتونه ؛ خواستید استفاده کنید. ( البته اگه قابل دونستید )
ولی از نظر من صبر کنید تا نسخه کاملش بیاید.

و یک نکته : این برنامه در حد 2 فرم ساخته خواهد شد؛ منظورم از کامل این است.
و دیتابیس اون رو به زودی قرار میدهم.
در دیتابیسش حرفهای زیادی برای گفتن خواهم داشت.

موفق باشید.
با تشکر پیشاپیش از همه ی شما دوستان
منتظرم


پاورقی :

لینک دانلود :

http://cid-d4aee6120707bb13.skydrive.live.com/self.aspx/.Public/SalesWinFormApp.zip

sia_2007
شنبه 28 آذر 1388, 21:59 عصر
دوستان اطمینان دارند که نظری ندارند ؟

sia_2007
یک شنبه 29 آذر 1388, 13:32 عصر
دوستان نظر دهی پیش کش
لطفا یک کامنت بگذارید که من بفهمم این رو میبینید !
این برنامه هر قسمتی از کدش یه نکتست.
نکنه لینکش خرابه ؟

mehdi.mousavi
یک شنبه 29 آذر 1388, 13:55 عصر
سلام خدمت دوستان عزیز از انتقادات سازنده یا غیر سازنده ؛ تند یا کند ، به شدت استقبال میشود.

سلام.
ببینید. این برنامه پر از ایراد هستش. فرصت کنم، چند تاشو براتون مینویسم. فعلا برای شروع، یه خرده در مورد SupressFinalize که اینقدر بی پروا ازش استفاده کرده اید، برامون توضیح بدید و اینکه چه ایده ای پشت استفاده از این متود توسط شما و بدین شکل بوده.

موفق باشید.

slashslash2009
یک شنبه 29 آذر 1388, 15:22 عصر
چرا اینقدر برنامه رو پیچوندی و از این همه class ومتد استفاده کردی پاکشون کن

sia_2007
یک شنبه 29 آذر 1388, 23:43 عصر
خوبه ؛ باز حداقل خیالم راحت شد.
در جواب این دوست عزیزمون باید بگم که هر کلاسی رو شما اظافه دیدید؛ بگویید تا من حذفش کنم.
خب
ببینید؛ حسب نیاز من در متد فاینالایز؛ یک سری کارها رو انجام میدم؛ که هنوز نوشته نشده اند.
اما بر اساس نوع طراحی ؛ اگر کاربر برای استفاده از کلاس ؛ از using استفاده کند؛ یا متد Destroy را صدا بزند؛ و یا Dispose ؛ دیگر نیازی به متد فاینالایز نیست.
خب ؛ اگر متد Dispose ؛ فراخوانی شود و نیازی به متد فاینالایز نداشته باشیم 2 تا مشکل داریم :
یکی این که کد فاینالایز ناخواسته اجرا میشود؛ که این مشکل کوچکی است و به راحتی حل میشود.
مشکل اصلی این است :
زمانی که یک کلاس متد فاینالایز داشته باشد؛ وقتی که GC سراغش میآید؛ (البته بعد از این که به زباله تبدیل شده بود) ؛ تازه یک دور فرصت میگیرد؛ تا متد فاینالایزش اجرا شود.
اما با این دستور به GC میگوییم نیازی به متد فاینالایز نیست.
و اجرایش نکن؛ و فرصت دوباره به وی نده.
حال ما سه حالت داریم :

1- بالاترین کارآیی:
اول متد Destroy ؛ سپس Dispose و فاینالایز هم اجرا نمیشود.

2- کارآیی متوسط :
اجرای متد Dispose ؛ و فاینالایز اجرا نمیشود.
من با پیاده سازی واسط IDisposeable ؛ امکان استفاده از بلاک using را نیز فراهم کرده ام.

3- کارآیی پایین :
وقتی است که برنامه نویس هیچ کدام از 2 متد اصلی را صدا نزده؛ فقط متد فاینالایز اجرا میشود.

واقعا خوشحال میشم هم شما؛ هم سایر دوستان نظریاتشون رو بگن.
البته اگه قابل میدونند.

نکته : خواهشا به SHAMSIUDT و کلاس Global گیر ندید.
یا هر نکته بی اهیمت دیگری.

منتظرم.

mehdi.mousavi
دوشنبه 30 آذر 1388, 11:44 صبح
زمانی که یک کلاس متد فاینالایز داشته باشد؛ وقتی که GC سراغش میآید؛ (البته بعد از این که به زباله تبدیل شده بود) ؛ تازه یک دور فرصت میگیرد؛ تا متد فاینالایزش اجرا شود. اما با این دستور به GC میگوییم نیازی به متد فاینالایز نیست. و اجرایش نکن؛ و فرصت دوباره به وی نده.

بسیار خوب... حالا اجازه بدید من در این مورد صحبت کنم. IDisposable در حقیقت بعنوان روشی استاندارد برای آزاد سازی "unmanaged resource" ها طراحی شد. در واقع منابعی که GC اونها رو بطور خودکار مدیریت نمیکنه! یکی از این منابع، handle به Database Connection ها هستش. همونطوریکه میدونید، بهترین حالت برای استفاده و بهره برداری از Connection Pooling، بستن Connection (بلافاصله) بعد از اتمام کار هستش. به این منظور، شما اومدید و در کلاس CustomerOper، اینترفیس IDisposable رو Implement کردید. تا اینجا درست...

اما باید ببینیم چطوری اینکارو انجام دادید. ابتدا، حالتی رو در نظر بگیرید که Dispose فراخوانی بشه. وقتی Dispose فراخوانی میشه، شما متود Destroy کلاس رو فراخوانی میکنید:


public void Destroy(ref CustomerOper myCusOper)
{
if (myCusOper == this)
{
Dispose();
myCusOper = null;
}
else
{
// throw Invalid Object
}
}


ابتدا به Prototype این تابع توجه کنید. این کار مطلقا صحیح نیست. طبق Design Pattern ها، اگر شما قراره اینکارو انجام بدید، باید جلوی دسترسی مستقیم به این متود رو (از بیرون از کلاس) بگیرید. بعبارت دیگه، استفاده کننده نباید اجازه داشته باشه تا از بیرون این متود رو Call کنه. اکنون به Formal Parameter های تابع نگاه کنید. شما Instance ای از کلاس CustomerOper رو دارید به این تابع پاس می کنید، که این کار هم صحیح نیست. بر اساس Design Pattern ها، تابع فوق باید بدین صورت نوشته بشه:


protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if (myCustomerDBOper != null)
myCustomerDBOper.Dispose();
}

myCustomerDBOper = null;
this.disposed = true;
}
}


disposed یک boolean flag هستش که نشون بده آیا Object مورد نظر Dispose شده یا خیر. چون اگر Dispose شده باشه، فراخوانی مجدد متود نباید کاری رو انجام بده. از طرف دیگه، متودهای Public ای که روی این کلاس دارید، پس از Dispose شدن Object نباید اجرا بشن. شما باید این مساله رو توسط flag مزبور (disposed) کنترل کنید. اگر Object شما Dispose شده بود و متود فرضا، UpdateCustomer شما فراخوانی شد، شما باید بلافاصله ObjectDisposedException رو throw کنید:


public Int32 UPDATECustomer(Customer myCustomer)
{
if (this.disposed)
throw new ObjectDisposedException("CustomerOper was disposed!");

//whatever code goes here...


خوب، برگردیم سر متود Dispose ای که در بالا به اون اشاره کردم. این متود، یا بصورت دستی فراخوانی میشه (که باید پارامتر true بهش پاس بشه)، یا توسط GC (با پارامتر false). وقتی دستی فراخوانی بشه، ما باید کلیه Resource های Managed و Unmanaged رو آزاد کنیم. وقتی بطور خودکار فراخوانی میشه، یعنی GC کار خودش رو انجام داده و Resource های Managed ما آزاد شده، فقط مونده Unmanaged Resource ها. پس اگر اینجا Unmanaged Resource ای داریم باید از شرش خلاص بشیم، و سپس کلیه Field ها رو null کنیم. اما متود مزبور کجا باید با پارامتر false فراخوانی بشه.

بازهم Design Pattern ها به شما میگه که باید اینگونه عمل کنید:


~CustomerOper()
{
Dispose(false);
}


بعبارت دیگه با افزودن یک destructor به کلاس، اونوقت این فرصت رو داریم که Dispose رو با پارامتر false فراخوانی کنیم. آخرین نکته، درست Implement کردن متود Dispose از اینترفیس IDisposable هستش:


public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}


این میشه یک کد خوب و قابل قبول. اما بازهم دقت کنید. پیاده سازی من هم از این الگو، thread-safe نیست. اگر این مساله برای شما مهم باشه، باید حتما هنگام Dispose کردن Resource اتون، حواستون به این مساله هم باشه.

اما این تازه اولین ایراد، به این کلاس بود. فرصت کنم، ایرادهای دیگه رو هم عنوان خواهم کرد.

موفق باشید.

پاورقی: این خودش شد یه مقاله!

sia_2007
دوشنبه 30 آذر 1388, 18:05 عصر
استفاده از فلگ اولین کدی بود که من استفاده کردم ؛ اما تمام این راه را رفته ام که از این فلگ استفاده نکنم.

با اجرای Dispose ؛ Destroy فراخوانی نمیشود.

---
اما استفاده از کد اول شما با متد فاینالایز خیلی جالب شده؛ حتما از این روش استفاده خواهم کرد.

مخصوصا که این هم کارآیی بالایی دارد.
---
دستتون درد نکنه
بعدی ها رو هم اگر اشاره کنید ممنون میشم.

mehdi.mousavi
سه شنبه 01 دی 1388, 12:24 عصر
استفاده از فلگ اولین کدی بود که من استفاده کردم ؛ اما تمام این راه را رفته ام که از این فلگ استفاده نکنم.

سلام.
این پاسخ شما، مثل آب سردی بود که روم ریختن. انتظار هر چیزی رو داشتم، الا همچین حرفی. شما برای عدم استفاده از یک boolean، (به فرض اینکه اینکارو هم درست انجام داده باشید)، نشستید N تا متود نوشتید و Call های بیخودی به برنامه تحمیل کرده اید؟؟؟

ما برای ضرب دو عدد، میتونیم توی یک لوپ، مقادیر مورد نظرمون رو با هم جمع کنیم و به همون جواب برسیم. مثلا جای اینکه بنویسیم 3*5 میتونیم توی یه لوپ، 5 بار 3 رو با هم جمع کنیم. نتیجه هر دوش عدد 15 هستش، اما این کجا و آن کجا!

واقعا متوجه نمیشم این حرفی که زدید یعنی چی...


با اجرای Dispose ؛ Destroy فراخوانی نمیشود.
چطور فراخوانی نمیشه؟ این کد شماست:

public void Dispose()
{
myCustomerDBOper.Destroy(ref myCustomerDBOper);
GC.SuppressFinalize(this);
}


من دفعه اول فکر کردم Destroy همون کلاس رو Call میکنید، اما دیدم اوضاع بی نظم تر از این حرفهاست. (دقت کنید، بی نظمی، متفاوت است از پیچیدگی)! شما دارید متود Destroy رو روی myCustomerDBOper فراخوانی میکنید و بعنوان پارامتر هم باز خودش رو پاس میکنید. این کار واقعا شگفت انگیزه...


اما استفاده از کد اول شما با متد فاینالایز خیلی جالب شده؛ حتما از این روش استفاده خواهم کرد.

کد اول؟ من در کل یک مطلب بطور کامل توضیح دادم. اول و دوم نداشت. ضمن اینکه این روشها بر اساس بهترین تجارب و الگوهای طراحی در دنیا به این نقطه رسیدن. در نتیجه طبیعی هستش که "جالب" و "کارآمد" باشن...

موفق باشید.

پاورقی: حداقل با گوش دادن به حرفهایی که زدم، نذارید CO2 های منتشر شده بیخود بوده باشه! :چشمک:

sia_2007
سه شنبه 01 دی 1388, 14:21 عصر
من یک کلاس رو به صورت کامپوزیشن ( یا هر اسم دیگری که بپسندید ) در یک کلاس دیگر قرار داده ام.
حال زمانی که کلاس اصلی از بین میرود؛ کلاسهایی که به صورت کامپوزیشن اظافه شده اند هم باید از بین بروند.
در واقع قبل از نابودی کلاس اصلی ( ظرف ) ؛ کلیه کلاسها و کانکشن ها و ... موجود در آن از بین میروند.

من قبل از نابود کردن CustomerOper ؛ CustomerDBOper رو با فراخوانی متد Destory خودش نابود میکنم.

متوجه نمیشوم اشکال این کار کجاست.

این برنامه به جای این که در سر در هر متودش یک if باشد ؛ دارای متودی است که اون رو پس از Dispose کردن ؛ آبجکتها را Destroy میکند.
و از رم بیرون میاندازد.

و در صورت فراخوانی متدهای public ؛ بدون گذاشتن شرط if ؛ خود به خود Null Pointer Exp میدهد.

من متوجه خوبیهای کد شما هستم ؛ اما متاسفانه در مورد کد های خودم هنوز متوجه اشکالی که شما میگویید نشده ام.

شما در کد خودتان ؛ ابتدا CustomerDBOper را Dispose کرده اید ؛ و سپس آنرا را برابر Null قرار داده اید.
کد من هم همین کار را میکند؛ با این تفاوت که با فراخوانی متد Destroy ؛ این کار در یک مرحله انجام میپذیرد؛ همین.

در حالی که من متد Destroy آنرا صدا زده ام ؛ و در متد Destroy ؛ Dispose هم صدا زده شده است.
در واقع من هم Unmanaged Resource های کلاس CustomerDBOper را که شاید به احتمال 1% باز بوده اند را بسته ام ؛ هم خودش را از RAM بیرون انداخته ام.

این هیچ شباهتی به یک Loop برای جمع 3 عدد به جای ضرب ندارد.

در واقع در این به اصطلاح حلقه جمع من ؛ هم Unmanaged Resource ها بسته میشوند ؛ هم خود کلاس از رم بیرون انداخته میشود.

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

چون فرض مثال در برنامه ؛ اگر کاربر متد Dispose ی را که شما نوشته اید فراخوانی کند؛ دیگر خود آبجکت اصلی (که در این جا CustomerOper است ) Null نمیشود؛(هر جند خود CustomerDBOper برابر Null گشته) و در رم هست ؛ و ما if میگذاریم تا اگر کاربر بعد از فراخوانی متد Dispose ؛ متد دیگری از کلاس را فراخوانی کرد؛ خودمان دستی یک خطا صادر کنیم.

در حالی که نیت از Dispose شدن چیست ؟
این که در رم باشد و جا بگیرد ؛ ولی اگر متدی از آن فراخوانی شد ؛ خودمان دستی خطا صادر کنیم ؟

نگاه کنید : شبه کد

Customer MyCustomer
CustomerDBOper myCustomerDBOper
myCustomerDBOper.Insert(MyCustomer) :A
myCustomerDBOper.Dispose
MyCustomerDBOper.Insert(MyCustomer) :A

حال در این جا ما چک کردن یک فلگ ؛ یک خطا صادر میکنم که برنامه نویس ساده دل فکر کند آبجکت در رم وجود ندارد و برنامه اش در کارآیی گوی سبقت را از همگان ربوده است.
تازه اگر شانس آورده باشیم و برنامه نویس خوبی باشد.
اگر از این عتیقه ها باشد ؛ که بعد از فراخوانی Dispose ؛ تمامی متدهای public برنامه را صدا میزند؛ تا اگر یادمان رفته شرط را در سر یکی از متدها چک کنیم ( به احتمال یک درصد )؛ قاه قاه به ریش ما بخندد که چی ؟
برنامه Dispose شده ؛ ولی متدهایش را میتوان فراخوانی کرد.
بعد هم Babylon را باز کند ؛ و به من و کارفرمای اصلیه خودش بگوید که معنی Dispose هر چه باشد ؛ این نیست که با یک فلگ ؛ نابود شدن کلاس را شبیه سازی کنیم.
که هم در رم باشد ؛ هر شرط های if ؛ برای برنامه سربار ایجاد کند.
فرض کنید یکی از متدها دارد هر یک ثانیه یک بار در تایمر اجرا میشود ؛ آن وقت تاثیر شگفت انگیز این if نمایان میشود.


Customer MyCustomer
CustomerDBOper myCustomerDBOper
myCustomerDBOper.Insert(MyCustomer) :A
myCustomerDBOper.Destroy(ref myCustomerDBOper)
MyCustomerDBOper.Insert(MyCustomer) :A


در این جا علاوه بر این که یک شرط if ؛ برای هر متد چک نمیشود؛ واقعا خود آبجکت در RAM وجود ندارد؛ و برنامه نویس NullPointerExp میگیرد. + Dispose هم شده.

واقعا شگفت انگیزه ؛ آب سرد ریخته شده ؛ حلقه جمع اعداد ؛ این عبارات حرفه ای نیستند؛ خواهشا مشکل را دقیق توضیح دهید.
من علاقه ای به استفاده از کد خاصی ندارم.
شما در ابتدا جوری گفته بودید که انگار من حوصله کپی پیست کردن یک if را ندارم.
در حالی که تمام نیت من این بوده که متد Dispose ای را که هیجان انگیزترین کارش بستن کانکشنی است که هر 10 میلیون بار یک بار ممکن است باز بوده باشد ؛ را ببند و برای یک کار ساده ؛ یک if را به تمامی متدهای public سربار کند ؛ و آبجکت خودش هم در رم باقی بماند؛ تازه بعد از قطع شدن ارجاعات قوی ؛ ارجاعات ضعیف به آن امکان پذیر باشد ؛ یک متدی داشته باشم که هم تمامی کارهایی را که شما انجام داده بودید را انجام دهد ؛ ( اگر نمیتواند بگویید ) ؛ هم مشکلات را کمتر کند.

واقعا اگر مشکلات این کد را برای من تشریح کنید ؛ قطعا از آن استفاده نمیکنم ؛ ( در یک فضای حرفه ای ؛ من تمامی مشکلات روش اصلی را که گفتم قبلا خودم هم بلد بودم را گفتم ) ؛ حال شما هم اشکالات را مطرح کنید ؛ بالاخره یکی از این دو بهتر است.

من در نهایت بی هیچ تعصبی آن را انتخاب میکنم.

خیلی ممنون که وقت میگذارید ؛ موفق باشید

mehdi.mousavi
سه شنبه 01 دی 1388, 17:34 عصر
سلام.
تاکید من در چک کردن flag در متود Dispose بود، نه در کلیه متودهای public کلاس، اگر چه، بسته به کلاس مورد نظر، اینکار میتونه خوب هم باشه. بعنوان نمونه کلاس MemoryStream مایکروسافت رو ببینید. بعد از اینکه یک MS رو Dispose میکنید، سعی کنید Length Property اونو set کنید. اونموقع متوجه میشید که ObjectDisposedException توسط سیستم throw میشه، یا با دیگر property هاش اینکارو کنید...

گذشته از اینها، روشی که من گفتم و از destructor استفاده میکنه، بکل متفاوت از چیزی هستش که شما پیاده سازی کرده اید. Destructore توسط GC فراخوانی میشه، اما Destroy شما توسط چه کسی فراخوانی میشه؟ توسط همون کسی که قراره Dispose رو Call کنه... خوب پس چرا از همون اول Dispose رو Call نمیکنه؟

به نحوه استفاده از disposed flag دقت کنید. من از اون flag استفاده کردم، تا کار dispose شدن رو فقط یکبار انجام بدم. وقتی GC میخواد Object منو Collect کنه، destructor من اجرا میشه و اونهم بنوبه خودش Dispose رو با پارامتر false اجرا میکنه تا برای دومین بار اقدام به پاک کردن Managed Resource هام نکنم. چون دفعه اولی که برنامه نویس Dispose رو فراخوانی کرده، اینکار انجام شده. دقت کنید، اینجا هم فرض بر اینه که متود Dispose توسط برنامه نویس Call میشه، چون IDisposable رو Implement کرده.

در هر حال، من نمیدونم، شما به چه چیزی از این کد افتخار می کنید:


CustomerDBOper operation = new CustomerDBOper();
operation.Destroy(ref operation);


پاورقی: من نمیتونم بابت هر یک ایرادی که در برنامه شما وجود داره، 6 صفحه مطلب بنویسم. لطفا اول از همه به Best Patterns & Practices رجوع کنید و طراحی اتون رو بر اون اساس انجام بدید. سپس برنامه رو اینجا Upload کنید و از دیگران نظر بخواهید. مطالبی که تازه تا اینجا مطرح شد، اولین ایرادی بود که به برنامه شما وارده.

sia_2007
چهارشنبه 02 دی 1388, 00:16 صبح
OK؛
شما وقت بیشتری روی همان پروژه نگذارید؛ چون اینی که دست من 70% نسبت به قبلی تغییر کرده.
من تازه دو ماه و نیمه که برنامه نویسی رو شروع کردم.
اون هم با وقت محدود
به نظر میرسه شما کاربر دائمی سایت باشید؛
من در نهایت آخر تابستان سال آینده مزاحم شما میشوم.
چون وقتی کد خودم را میبینم که نسبت به آنی که دست شماست متفاوت است ؛ دلیلی برای دفاع نمیبینم.
خیلی ممنون ؛ موفق باشید