ورود

View Full Version : كلاس Entity



mohamad100000
پنج شنبه 06 تیر 1392, 12:36 عصر
سلام دوست عزيز

يه سوال تخصصي داشتم كه اميدوارم بتوني جوابشو بدي

من دارم مهندسي نرم افزار مي خونم و برنامه هايي كه مدلسازي مي كنم با سي شارپ كدنويسي مي كنم، و در طراحي نمودار هاي Sequence ، سه نوع كلاس را به ما معرفي كردند، كلاس كنترلي ،كلاس entity و كلاس هاي واسط.

فرض بر اين بگيريم كه براي ذخيره داده ها در ديتا بيس، يك كلاس كنترلي ديگر بنام Transaction تعرف مي كنيم كه كلاس entity فقط بيانگر منطق برنامه كاربردي باشد نه مطنق ذخيره سازي، و براي ذخيره سازي داده ها از كلاس Transaction استفاده مي كنيم.يعني از كلاس Entity به كلاس Transaction بفرستيم.


نكته مهمي ديگر اينست : كه كلاس ها نبايد بهم وابستگي داشته باشنده و تنها وابستگي اون ها از طريق كلاس كنترلي برنامه است كه سناريو جريان كاررا در خود تعريف كرده است.

يعني به اين شكل:
داده از واسط به وسيله كلاس كنترلي گرفته مي شود.

در كلاس entity ارزيابي و ذخيره مي شود.سپس

دوباره كلاس كنترلي ، كنترل برنامه را بدست مي گيرد

به كلاس Transaction دستور مي دهد، داده ها را از كلاس Entity دريافت و در ديتابيس ذخيره كند.

اما وقتي در محيط سي شارپ مدلم رو پياده سازي مي كنم با يك مشكل خيلي حاد برخورد مي كنم. و اون مشكل اينه كه در ارتباطات بين كلاس ها ، كلاس Entity من توسط مخرب از بين ميرود.

وقتي كنترل برنامه از Entity به كلاسي ديگر واگذار مي شود. مخرب ديگر با توجه به اينكه هيچ اشاره گري به اون توجه اي نداره ، شي كلاس Entity رو از بين ميبره.

حال سوال مبهم من براي كلاس Entity اينه‌:

چگونه يك كلاس Entity پايدار رو در سي شارپ تعريف كنيم؟

كلاسي كه در پس زمينه كلاس هاي ديگه فعال باشه و توسط مخرب از بين نره؟

كلاسي كه طول عمر اون بدست مخرب نباشه ؟

يك كلاس كه تا آخر سناريو من دوام بياره؟

cups_of_java
پنج شنبه 06 تیر 1392, 14:04 عصر
با روشی که شما مدل کردی، طبیعی هستش که طول عمر کلاس Entity دست Controllerت باشه.یعنی در حین سناریو وقتی بهش احتیاج هست اون رو ایجاد میکنه و کارش که تموم شد رفرنسشو بر میداره.
به این نکته هم توجه داشته باش که شما نباید سعی کنی یه شی Entity داشته باشی همیشه زنده باشه تا همه جا ببینیش (این خلاف اصول شی گرایی هستش) بلکه شما باید هرجا دیتا داری و نیاز یه Entity داری یدونه new کنی ازش استفاده کنی و بعد سریع ولش کنی. پاک هم شد بشه! در این حالت شما باید رفرنس های غلط و سراسری توی برنامت تعریف نکنی! (متوجه منظورم میشی؟)

توی اغلب فریم ورک ها روش های Dependency Injection برای حل اینجور مشکلاتی بوجود اومده و شما می تونی ازشون استفاه کنی.

mohamad100000
پنج شنبه 06 تیر 1392, 14:42 عصر
بله تا حدودي متوجه شدم ، يه جا نوشته بود مدلسازي كردن مثل تعريف يك انشا است و هر كسي مي تونه به سبك خاص خودش سناريوي برنامه شو مدلسازي كنه.
اما تمامي رفرنس هايي متعلق به مهندسي نرم افزار رو تماشا كردم، كه همه به اين سبك سناريو ها را مدلسازي كردنند. يك روش استوار و قابل استفاده مجدد هستش اما من با كلاس Entity با مشكل بر مي خورم.
106222
وقتي كه كلاس كنترلي ما اطلاعات را به وسليه Set info به كلاس Entitiy يا كلاس Account مي فرسته، اينجاست كه كلاس Entity ما تغذيه اطلاعاتي ميشه و رفتار هاي دخلي خودشو انجام ميده.
حال مي خواهيم اين اطلاعات را در انباره ذخيره كينم.
دوباره بوسيله كلاس كنترلي اقدام به ذخيره اطلاعات مي كنيم.اينبار يك دستور Save Info به كلاسي كه مسئول تراكنش هاي ذخيره و حذف هستش مي فرستم.
و وقتي كه كلاس Transaction از كلاس accont در خواست اطلاعات مي كند :
اطلاعاتي كه بدست مي آيد Null هستنند.
هنگامي كه دستور Save Info را اجرا كرديم، و كنترل برنامه به دست كلاس Transaction افتاد، اشاره گر آبجكت مربوط به كلاس entity ما از بين رفت ، و مخرب نيز اطلاعات آن كلاس را نابود مي كند.
اگر ما در كلاس Transaction يك شي از نوع كلاس Entity تعريف كنيم و بوسيله متد هاي دستيابي get بخواهيم فيلد هايمان را بخوانيم، مي بنيم كه فقط null را برگشت مي دهد.
پيشنهاد شما براي اين سناريو چيست؟

cups_of_java
پنج شنبه 06 تیر 1392, 20:03 عصر
می دونم چی میگی! من جواب قبلیم رو بیشتر توضیح میدم برات:
ببین مشکل همون خط عمر Account روی نمودارت هستش دیگه، دو تکه کوچیک رو نشون میده! این یعنی دو تا طول عمر کوتاه از دو تا شی! طبیعی می تونه باشه که Transaction در شی دوم Account دیگه اطلاعات فبلی رو نبینه. خودتم داری تو نمودار می بینی که یک شی جدید (طول عمر جدید) شروع شده. نمی دونم چرا اینطوری نوشتی و به این مشکل برخورد کردی، تو اکثر مثال ها ببینی اصن همچین مشکلی پیش نمیاد. برای حل دو تا راه به ذهن می میرسه:
یک اینکه حساسیتی روی پاس کردن شی Account به کلاس Transaction نداشته باشی، و از این dependency نترسی (ذاتا تو این مدل بین مدل ها (Entity) و کلاس های مرتبط با پایگاه های داده وابستگی هست. الگو های طراحی مختلفی هم برای مدیریت این وابستگی ها هست. ADO، ORM. ...) تو این حالت باید بنویسی:
e = new Entity();
e.setInfo( ... );
transaction.saveInfo( e );

راه دو اینه که کلاس Entity رو Singleton پیاده سازی کنی، تا همیشه یک شی ازش وجود داشته باشه، در این حالت طول عمرش توی نمودارت کامل روی میله عمودی نشون داده خواهد شد، که اصن این راه رو پیشنهاد نمی کنم. (موجودات سراسری و سینگلتون مرموزن و مضر ترین کد ها در نگهداری و توسعه کد هستند)

اما نظر خود من اینه که اصن شما نباید از کلاس کنترلرت، کلاس Transaction رو ببینی! کلاس های نگهداری داده روی پایگاه های داده، سطح پایین هستند و جزییات ماندگاری و دسترسی داده ها رو دارن، به کنترلر اصن ربطی نداره و نباید داشته باشه. این کارت غلطه! شما توی کنترلر باید مدل (Entity) رو ببینی. وقتی کنترلر به مدل گفت save کن خودت رو، مدل خودش باید بتونه خودش رو به جوری save کنه. حالا با Transaction با هر الگوی دیگه ای مثل ADO یا ... (من خودم از ADO خوشم نمیاد و همه جا نباید ازش استفاده کرد)
میشه اون مطلبی که باعث شده کنترلرت رو وصل کنی به Transaction بهم لینکشو بدی. احساس می کنم اشتباهی صورت گرفته

mohamad100000
جمعه 07 تیر 1392, 00:24 صبح
خيلي ممنون از راهنماييتون
بنا به تعريف شما و تعاريف بزرگان، استفاده از يك كلاس با طول عمر طولاني يا آبجكتي از يك كلاس بصورت سراري برخلاف قوانين شي گرايي است.
اون مطالب به اين عنوان مطرح شدن:
كلاس كنترلي : بايد سناريو و دستورات را در خود تعريف كند.
كلاس ها نبايد به هم وابستگي داشته باشند چونكه اين وابستگي استفاده مجدد كلاس را در برنامه ديگر از بين مي برد.
تا حد امكان وابستگي نبايد بين كلاس ها وجود داشته باشد، مخصوصا وابستگي حلقوي بين دو كلاس ، در اين صورت هيچ كدام از كلاس ها به تنهايي در برنامه ديگر قابل استفاده مجدد نيستنند.
-------------
دوست عزيز ما به كلاس هاي كنترلي لقب كلاس manager يا مدير اختصاص ميديم.اگر چه كلاس transaction يك كلاس مدير است، اما نميدونم چرا نويسنده كتاب اينجوري سناريو هاشو بيان كرده.كه كلاس entity نبايد وابستگي به كلاس Transaction داشته باشه.
بلاخره اگر از ديدگاه كلاس هاي مدير يا كنترلي به مسله نگاه كنيم مي بنيم كه ارتباط بين كلاس entity و كلاس مدير transaction يك ارتباط ، مجاز و منطقي هست.
--------------------
سوال ديگر من اينست:
اگر سناريوي بالا را فرض مثال بگيريم : كه قبل از اقدام به ذخيره سازي داده ها بايد داده ها رو ارزيابي كنيم: كه اين ارزيابي به دو شكل باشد:
1- داده ها نام و نام خانوادگي را چك كنيم تركيبي از حروف مجاز باشند:
آيا اين چك كردن بايد در كلاس كنترلي صورت بگيره؟ ياد در همون كلاس واسط ؟ يا در اعتبار سنجي كلاس Entity ؟

2- فرض بر اين بگيريم كه نام و نام خانوادگي در ديتابيس بايد غير تكراري باشند:
آيا اين چك كردن كه نيازمند پل ارتباطي با ديتابيس هستش، بايد در كلاس كنترلي بين واسط و entity باشه؟ يا در كلاس كنترلي Transaction؟ يا شايد نيازمند يك كلاس جديد باشه؟
ممنون ميشم به اين دو سوال هم جواب بديد.

cups_of_java
جمعه 07 تیر 1392, 04:47 صبح
كلاس ها نبايد به هم وابستگي داشته باشند چونكه اين وابستگي استفاده مجدد كلاس را در برنامه ديگر از بين مي برد.
تا حد امكان وابستگي نبايد بين كلاس ها وجود داشته باشد، مخصوصا وابستگي حلقوي بين دو كلاس ، در اين صورت هيچ كدام از كلاس ها به تنهايي در برنامه ديگر قابل استفاده مجدد نيستنند.

این جملات رو در عمل باید اینطوری درک کنید: آیا این ممکنه که کلاس ها واقعن در عمل به هم وابستگی نداشته باشن؟ نه! بدون وابستگی که چیزی به چیزی نمی چسبه! برنامه کار نمی کنه. پس منظور این جملات این هست که وابستگی ها باید درست و به جا تعریف بشن. وابستگی حلقوی چیز بدیه! این به کلی درسته.


دوست عزيز ما به كلاس هاي كنترلي لقب كلاس manager يا مدير اختصاص ميديم.اگر چه كلاس transaction يك كلاس مدير است، اما نميدونم چرا نويسنده كتاب اينجوري سناريو هاشو بيان كرده.كه كلاس entity نبايد وابستگي به كلاس Transaction داشته باشه.

اگر وارد جزییات بشیم، کلاس های کنترلر با مدیر ها یه فرق هایی دارند. اما در کل کنترلرها چون محل دراوردن سناریو هستن و برای وصل کردن تکه های مختلف سناریو به هم تلاش می کنند، بسیار نقاط حساسی هستن! اصولن به اکثر پروژه ها نگاه کنید، کنترلر ها کثیف ترین و شلوغ ترین کد ها رو دارن! کنترلر ها پتانسیل بالایی برای بهم ریختن شی گرایی تو کد برای شما ایجاد می کنند. هی میرین به سمتی که کد رو توی کنترلر بنویسین.
پروژه بزرگ که میشه به جایی میرسه که تمام Business Logic توی کنترلر هاست و شما گم میشین و نمی تونین نرم افزار رو گسترش بدین یا تغییر بدین.
به خاطر همین تو خیلی از منابع امروزی، دیگه کنترلر ها موجودات بدی اسم برده میشن. حتی تو مدل های امروزی کمتر ازشون استفاده میشه. (مثلن مدل MCV سال هاست که داره تبدیل به مدل های MVVM، MVP، MV میشه)
اصولن از این سه نوع کلاس که در UML جا افتاده تو روش های RUPو USDP استفاده میشده و حتی تو کتاب های USDP میگن منطق برنامه (Business Logic) میره تو کنترلر!!! ولی بعدن دیدن که اینکار اون خطرات بالا رو داره، و خیلی روش ها اصن اینا رو توصیه نمی کنن به خاطر همین پیچیده شدن کنترلر ها. کنترلر فقط باید توالی اجرای سناریو رو کنترل کنه، یا تراکنش کاربر با سیستم رو جمع و جور کنه! باید کدش کم حجم باشه.
اما کد های مربوط به داده ها و مدل ها و نگهداریشون تو دیتابیس به Entityها مربوط میشه و اصن کنترلر نباید وابستگی بهشون داشته باشه. (برعکس چیزی که شما گفتی) پس ممکن هست که اون کلاس Transaction که شما میگی اصن موضوعش چیز دیگه ای هست، یا شما بد متوجه شدی با ید توضیح دادی. ولی اگه کار save رو انجام میده باید Entity بهش وابسته باشه نه کنترلر
کتابی که میگین چیه؟ بگید من نگاش کنم.



آيا اين چك كردن بايد در كلاس كنترلي صورت بگيره؟ ياد در همون كلاس واسط ؟ يا در اعتبار سنجي كلاس Entity ؟
Validationها باید توی خود کلاس Entity باشن. (یا هر کلاس مستقلی که مستقیم کارش چک صحت اطلاعات اون Entity باشه) البته توی فریم ورک ها روش هایی برای اعمال انواع Validation سمت سرور و کلاینت هست، بعضی با استفاده از همون Entityها، بعضی ها هم اصن کلاس های مختص validation برای کلاینت دارن.


آيا اين چك كردن كه نيازمند پل ارتباطي با ديتابيس هستش، بايد در كلاس كنترلي بين واسط و entity باشه؟ يا در كلاس كنترلي Transaction؟ يا شايد نيازمند يك كلاس جديد باشه؟
این چک باید توی کلاسی باشه که دیتا رو توی پایگاه داده می نویسه. نه توی کنترلر

mohamad100000
جمعه 07 تیر 1392, 13:28 عصر
اسم كتاب : مرجع كامل uml with rational rose است.
106263
پس با توجه به گفته ها : سناريو رو مي تونيم به اين شكل تغيير بديم.
اما اگه به كلاس transction توجه كنيم قبل از اينكه داده ها رو در ديتابيس ذخيره كنيم، ابتدا متد CheckAbsenceInfo اجرا ميشه و از نبود اين اطلاعات در انباره، براي ادامه سناريو مطمن ميشه.
سپس متد SaveToDisk رو فرخواني مي كنه و اقدام به ثبت اطلاعات در انباره مي كنه.
براي اينكه بتونيم اين اطلاعات رو در هر دو اين متد ها استفاده كنيم،مجبوريم يك فيلد از نوع كلاس Account تعريف كنيم، و در متد SaveInfo اون فيلد رو مقدار دهي كنيم،سپس بلافاصله متد CheckAbsenceInfo رو فرخواني كنيم.
نظر شما چيه؟
-------
توي مطاب گفته بود،كلاس هاي كنترلي با توجه سناريويي كه انجام ميدن، مي تونيم پسوند manager رو بهش اختصاص بديم.
يعني مي تونيم با توجه سناريوي ما، كه مربوط به ثبت اطلاعات يك شخص هستش،نام RegisterManager رو به كلاس كنترلي اختصاص بديم.
جالبه در كلاس كنترلي ما با فرخواني متد SaveInfo از واسط، اطلاعات رو دريافت مي كنه، و بعد مستقيم اونا رو به كلاس entity ميفرسته.... بدون هيچ ازريابي و پردازشي بر روي اين اطلاعات.
آيا بهتر نيست تغذيه اطلاعاتي كلاس Entity ، رو مستقيم از واسط انجام بديم؟
كلاس كنترلي چه نقشي در تغذيه اطلاعات دارد ؟

cups_of_java
جمعه 07 تیر 1392, 19:55 عصر
نظر شما چيه؟
احیانن توی مرحله 1.1.1.1.1.1.1 از داخل کلاس Transaction متدی از کنترلر رو که صدا نزدی؟ کسی سمت Entity و دیگه بدتر پشت Entity (مثل Transaction) نباید کنترلر رو ببینه. نتایج عمل save به صورت مقدار برگشتی دست کنترلر قرار میگیره.



آيا بهتر نيست تغذيه اطلاعاتي كلاس Entity ، رو مستقيم از واسط انجام بديم؟
كلاس كنترلي چه نقشي در تغذيه اطلاعات دارد ؟
نقش کنترلر همینه در اصل که بتونه سناریو رو بسازه دیگه، یعنی دیتا رو از واسط بگیره و بدونه به کدوم Entity بده، بعدش چی کار کنه....! خلاصه این توالی کارشه! اما به همون دلایلی که بالا گفتم، توی مدل های مدرن امروزی کنترلر ها دارن کمرنگ تر میشن و گاهن شما میبینی Entity با کلاس های دیگه ای مستقیم به واسط وصل هستن. (MVP، MVVMو...) البته این کار باید طبق همین الگو ها انجام بشه، اگه شما مستقیم کلاس Entity رو وصل کنی به واسط، بدتر میشه و عملن کنترلر رو حذف کردی، چیزیم جاش نزاشتی، پس کلی کد پخش و پلا میشه بین ایندو، و نگهداری و توسعه کدت سخت میشه!

mohamad100000
شنبه 08 تیر 1392, 01:02 صبح
بله من در بيشتر برنامه اي كه طراحي كردم از طرف transaction پيغام به سمت كنترلر ارسال كردم.
پيغام هايي به اين شكل:


خطاي جدي رخ داده است سناريو رو به كل متوقف كن :صدا زدن seriousError() در كلاس transaction به سمت كلاس كنترلي



خطلا warning رخ داده است از واسط سوال كن و در صورت تمايل سناريو را ادامه بده ، در غير اينصورت سناريو رو متوقف كن


بعنوان مثال وقتي كه در كلاس transaction چك مي كنيم و مي بنيم، كه اين اطلاعات قبلا در ديتا بيس ذخيره شده : از طرف transaction چند تا بيت به كلاس errorDialge فرستاده مي شه،
و كلاس errorDialge بر اساس اطلاعات دريافتي كد خطا محاسبه مي كنه و اونو به Transaction بر مي گرودنه،
كلاس transaction بر اساس كد خطا واكنش نشون ميده
مثلا كد برگشتي 7 بيان گر خطاي جدي هستش و متد StopSenario رو در كنترلر به منظور خاتمه سناريو صدا ميزنه ، و كنترلر هم در اون متد يك پيغام به واسط مي فرسته كه پيغام خطا به كاربر اعلام ميشه.
يا بعنوان مثال ديگر كد هاي خطاي 1،3،5،6 كد هاي خطاي warning هستش، و وقتي كلاس errorDialog يكي از اين كد ها رو به Transaction برميگردونه،
Transaction براي ذخيره سازي نياز به جواب كاربر داره ، و از طريق كنترلر يك پيغام به واسط فرستاده ميشه و جواب اون:
Boundary>Control>Transaction برگشت داده ميشه ، اونوقت transaction تصميم ميگره با توجه به جواب كاربر اطلاعات را ذخيره كنه، يا نه.
عمليات خطايابي در كلاس ErrorDialog انجام ميشه: من سخت افزار خوندم و خطايابي بيتي رو در برنامه استفاده كردم
از طرف transaction سه تا بيت يا سه عدد : 0 يا 1 فرستاده ميشه:
106304
چرا سه بيت؟
فرض بگيريم نام ، نام خانوادگي، شماره تلفن رو داريم، اگر هر يك از اين سه فيلد در ديتابيس وجو داشته باشد 1 رو ErrorDilog ميفرسته در غير اينصورت اگر هر يك از فيلد ها در ديتابيس وجود نداشته باشن 0 رو ميفرسته: مثلا 011 به اين معناست نام وجو ندارد ، نام خانوادگي وجود دارد، شماره تلفن مربوط به اين نام خانواگي هم وجود دارد، كه حكايت از تكراري بودن اطلاعات دارد، و جمع بيتي 110 عدد 6 ميشه،كلاس Transaction با عدد 6 خطاي Wraning رو متوجه ميشه و طبق گفته ها ي بالا به كاربر اعلام مي كنه.
-----
گفته شما در اين مورد كه نيازمند چند تا رفتار متفاوت براي ذخيره سازي هستيم چي هست؟
اگر بخواهيم فقط مطمن باشيم كه اطلاعات در انباره ذخيره شده است يا نه؟با يك برگشتي بولين مي تونيم اينكارو انجام بديم، اما براي چند رفتار متفاوت مثل گفته هاي بالا چي سناريوي رو پيشنهاد مي كنيد؟

cups_of_java
شنبه 08 تیر 1392, 21:31 عصر
این جزییاتی که شما نوشتی نشون میده تعریف شما از کلاس transaction با اون چیزی که من فهمیدم فرق داره احتمالن!
به هر صورت شما ممکنه به یک یا دو کلاس در کنار Entity همیشه اجتیاج داشته باشی برای Validation و منطق های این تیپی! وضعیت های مختلف رو هم به صورت مقدار برگشتی باید بفرستی، یه کلاس AccountValidationResult مثلن که فیلد های متن پیام و وضعیت و نوع خطا رو داره مثلن. شی ای از این کلاس ایجاد میکنی توی Transaction و بر میگردونی به عنوان مفدار برگشتی تا برسه دست کنترلر.

من کلیات رو به شما میگم، اما قرار نیست براتون نرم افزار رو مدل کنم یا نمودار بکشم. شما با مطالعه و نمونه مدل کنید، مشکلاتتون رو بپرسید. اینطوری ممکنه از اصل اون مدلی که دارین انجامش می دین جدا شین.

mohamad100000
یک شنبه 09 تیر 1392, 19:38 عصر
پس نهايتا نتيجه ميگيريم:
در كلاس entity : بايد داده ها دريافت بشه و هر گونه Validation كه نياز باشه در اين كلاس انجام بگيره، در قانون شي گرا ، هر كلاس بايد به اندازه اي خاص خودش رفتار هايي رو داشته باشه كه بعنوان يك آبجكت استوار از اون استفاده كنيم.

كلاس transaction : يك كلاس حساسه كه ميشه گفت آخر خط يك سناريو هست ، وقتي از اين كلاس استفاده مي كنيم كه بخواهيم فقط و فقط عمليات حذف ، اضافه ، آپديت و دليت انجام بديم، هيچ گونه validation اي اين كلاس به عهده نداره، همچنين مجاز نيست با كلاسي بغير از كلاس entity در ارتباط باشه.