PDA

View Full Version : مشکل در متد abstract



haamidd
جمعه 03 مهر 1394, 20:52 عصر
سلام و عرض ادب.


مثلا من یک کلاس به نام User دارم که توش 4 تا متد abstract با نام های M1 و M2 و M3 و M4 تعریف شده و 4 تا هم کلاس دیگه دارم که از متد کلاس قبلی به ارث میرن و متد های abstract قبلی توشون کامل میشن.
حالا من مثلا تو یکی از کلاس های زیر مجموعه نخوام متد M4 پیاده سازی بشه و اصلا وجود نداشته باشه باید چیکار کنم؟

خلاصه مشکلم: i don't need to override all the methods in abstract class

ممنون

Felony
شنبه 04 مهر 1394, 11:08 صبح
متدها رو به صورت Virtual تعریف کن :


public abstract class User
{
public virtual bool M1()
{
throw new NotImplementedException();
}
public virtual bool M2()
{
throw new NotImplementedException();
}

public virtual bool M3()
{
throw new NotImplementedException();
}

public virtual bool M4()
{
throw new NotImplementedException();
}
}

public class Test : User
{
public override bool M1()
{
return true;
}

public override bool M4()
{
return true;
}
}

SabaSabouhi
شنبه 04 مهر 1394, 13:19 عصر
سلام و عرض ادب.


مثلا من یک کلاس به نام User دارم که توش 4 تا متد abstract با نام های M1 و M2 و M3 و M4 تعریف شده و 4 تا هم کلاس دیگه دارم که از متد کلاس قبلی به ارث میرن و متد های abstract قبلی توشون کامل میشن.
حالا من مثلا تو یکی از کلاس های زیر مجموعه نخوام متد M4 پیاده سازی بشه و اصلا وجود نداشته باشه باید چیکار کنم؟

خلاصه مشکلم: i don't need to override all the methods in abstract class

ممنون

سلام
شما باید مفاهیم Object Oriented رو دقیق درک کنی تا به اشتباه نیافتی. ضمن تایید نوشته‌های دوستمون آقای تاجیک
کمی قضیه رو باز می‌کنم.
وقتی یک متد به صورت abstract تعریف می‌شه، معنیش اینه که کلاسی که از این کلاس ارث می‌بره، حتماً و حتماً باید اون رو
پیاده‌سازی کنه.
اگه به صورت virtual تعریف کنی، معنیش اینه که کد پیش‌فرضی برای این متد نوشتی و کلاس‌هایی که از این کلاس ارث می‌گیرن
اگه بخوان می‌تونن اون رو override کنن. و در اینجا برعکس حالت abstract اختیار وجود داره.

در یک کلاس abstract زمانی متد abstract تعریف می‌کنیم که قصد نداریم اون رو به هر صورتی بنویسیم ولی
می‌خواهیم ازش استفاده کنیم ( مثلاً در متدهای پیاده‌سازی شده در همون کلاس abstract ). و مسوولیت
پیاده‌سازی رو به کلاس‌های به‌ارث‌گیرنده واگذار می‌کنیم.

صبا صبوحی

ASKaffash
یک شنبه 05 مهر 1394, 07:32 صبح
سلام
درست است که با Virtual می توان پیاده سازی را انجام داد و از منطق والد استفاده کرد ولی هنوز بخش دوم سئوال ایشان پاسخ داده نشده است یعنی متد اصلا نباید وجود داشته باشد بنابراین باید بجای کلاس abstract از دو اینترفیس استفاده شود و کلاس مورد نظر فقط از اینترفیس اول ارث بری کرده و بنابراین پیاده سازی فقط سه متد لازم است

FastCode
یک شنبه 05 مهر 1394, 10:23 صبح
abstract class UserBase:
abstract void M1
abstract void M2
abstract void M3

abstract class User : UserBase:
abstract void M4

class C1 : User

class C2 : User

class C3 : User

class C4 : UserBase


چرا اینقدر کار رو میپیچونید.سوال خیلی سادست

ASKaffash
یک شنبه 05 مهر 1394, 14:51 عصر
سلام
روش شما سطح وراثت را یک سطح افزایش داد

FastCode
یک شنبه 05 مهر 1394, 18:34 عصر
سلام
روش شما سطح وراثت را یک سطح افزایش داد
خب؟ مگه مشق دانشگاه بوده؟
16 بایت حافظه + یک lookup + یک entry در vtable
این روش تمیزترین کاربری رو داره

ASKaffash
دوشنبه 06 مهر 1394, 06:39 صبح
سلام
1- همیشه آسانترین راه حل بهترین نیست
2- افزایش سطح ورائث افزایش منابع را خواهد داشت

FastCode
چهارشنبه 08 مهر 1394, 11:31 صبح
سلام
1- همیشه آسانترین راه حل بهترین نیست
2- افزایش سطح ورائث افزایش منابع را خواهد داشت

۱.بله.ولی نه در این مورد.
۲.خیر
در این مورد دقیقا همونقدر که گفتم هزینه داره. در صورتی که هر interface به اندازه 8 + تعداد متدها به اضافه دو lookup به اضافه 8 بایت برای هر overload به اضافه یک cast و یک lookup اضافه در interface table برای بررسی ارثبری اضافه موقع استفاده هزینه خواهد داشت.

o(log(a+b))در برابر
o(log(a)+log(b)) که میشه
o(log(ab)) که فکر میکنم واضحه
من سورس کد memory allocator های .net و mono رو دقیق مطالعه کردم.

ASKaffash
شنبه 11 مهر 1394, 06:44 صبح
علیکم سلام
لینک را قرار دهید تا همه ما هم مطالعه کنیم

rahnema1
شنبه 11 مهر 1394, 08:59 صبح
سلام
فکر کنم استفاده از interface بهتره هم خوانا تره و هم پیچیدگی کمتر داره. مقدار کدی که باید نوشته بشه کمتره کد زیر را هم واسه میزان مصرف حافظه نوشتم که کلاس abstract حافظه بیشتری مصرف می کنه
روی سیستم من دو عدد زیر ایجاد شد که اولی مصرف حافظه abstract و دومی هم مربوط به interface هست
3592192
3391488

using System;
using System.Diagnostics;
namespace memoryTest
{
/**********abstract class************/
abstract class UserBase{
public abstract void M1();
public abstract void M2();
public abstract void M3();
}
abstract class User : UserBase{
public abstract void M4();
}
class CAbstract1 : User{
public override void M1(){}
public override void M2(){}
public override void M3(){}
public override void M4(){}
}
class CAbstract4 : UserBase{
public override void M1(){}
public override void M2(){}
public override void M3(){}
}
/**********interface ************/
interface IUserBase{
void M1();
void M2();
void M3();
}
interface IUser {
void M4();
}
class CInterface1 : IUserBase, IUser{
public void M1(){}
public void M2(){}
public void M3(){}
public void M4(){}
}
class CInterface4 : IUserBase{
public void M1(){}
public void M2(){}
public void M3(){}
}


public class Program
{
public static long testInterface(int n)
{
long befor = 0 , after = 0;
Process p = Process.GetCurrentProcess();
p.Refresh();
befor = p.WorkingSet64;
CInterface1[] c1 = new CInterface1[n];
CInterface4[] c4 = new CInterface4[n];
for (int i = 0; i < n; i++) {
c1[i] = new CInterface1();
c4[i] = new CInterface4();
}
p.Refresh();
after = p.WorkingSet64;
CInterface1 x1 = c1[n - 1];
CInterface4 x4 = c4[n - 1];
return after - befor;
}
public static long testAbstract(int n)
{
Process p = Process.GetCurrentProcess();
long befor = 0 , after = 0;
p.Refresh();
befor = p.WorkingSet64;
CAbstract1[] c1 = new CAbstract1[n];
CAbstract4[] c4 = new CAbstract4[n];
for (int i = 0; i < n; i++) {
c1[i] = new CAbstract1();
c4[i] = new CAbstract4();
}
p.Refresh();
after = p.WorkingSet64;
CAbstract1 x1 = c1[n - 1];
CAbstract4 x4 = c4[n - 1];
return after - befor;
}
public static void Main(string[] args)
{
int n = 100000;
Console.WriteLine(testAbstract(n));
Console.WriteLine(testInterface(n));
}
}
}

FastCode
یک شنبه 12 مهر 1394, 08:15 صبح
تستتون مشکل داره.
WorkingSet هیچ ربط مستقیمی به مقدار Allocate شده در heap های gc نداره.
از یک memory profiler استفاده کیند.
و حتما یک مرحله warm-up هم انجام بدید.

rahnema1
یک شنبه 12 مهر 1394, 12:20 عصر
ممنون از توصیه تون
از warmup استفاده کردم میزان مصرف حافظه در هر دو یکسان شد
همچنین به جای WorkingSet64 از GC.GetTotalMemory استفاده کردم، باز هم مقدار مصرف حافظه یکسان نشون داده شد
واسه پروفایل حافظه هم از process hacker2 استفاده کردم که باز هم میزان مصرف حافظه یکسان بود!

FastCode
یک شنبه 12 مهر 1394, 15:43 عصر
متشکرم. اگر کد جدید رو بزارید من میتونم با لینوکس هم تست کنم. دوست دارم ببینم نتیجه اونجا چطوره

rahnema1
یک شنبه 12 مهر 1394, 16:35 عصر
using System;
namespace memoryTest
{
/**********abstract class************/
abstract class UserBase{
public abstract void M1();
public abstract void M2();
public abstract void M3();
}
abstract class User : UserBase{
public abstract void M4();
}
class CAbstract1 : User{
public override void M1(){}
public override void M2(){}
public override void M3(){}
public override void M4(){}
}
class CAbstract4 : UserBase{
public override void M1(){}
public override void M2(){}
public override void M3(){}
}
/**********interface ************/
interface IUserBase{
void M1();
void M2();
void M3();
}
interface IUser {
void M4();
}
class CInterface1 : IUserBase, IUser{
public void M1(){}
public void M2(){}
public void M3(){}
public void M4(){}
}
class CInterface4 : IUserBase{
public void M1(){}
public void M2(){}
public void M3(){}
}


public class Program
{
public static long testInterface(int n)
{
long befor = 0 , after = 0;
befor = GC.GetTotalMemory(true);
CInterface1[] c1 = new CInterface1[n];
CInterface4[] c4 = new CInterface4[n];
for (int i = 0; i < n; i++) {
c1[i] = new CInterface1();
c4[i] = new CInterface4();
}
after = GC.GetTotalMemory(false);
CInterface1 x1 = c1[n - 1];
CInterface4 x4 = c4[n - 1];
return after - befor;
}
public static long testAbstract(int n)
{
long befor = 0 , after = 0;
befor = GC.GetTotalMemory(true);
CAbstract1[] c1 = new CAbstract1[n];
CAbstract4[] c4 = new CAbstract4[n];
for (int i = 0; i < n; i++) {
c1[i] = new CAbstract1();
c4[i] = new CAbstract4();
}
after = GC.GetTotalMemory(false);
CAbstract1 x1 = c1[n - 1];
CAbstract4 x4 = c4[n - 1];
return after - befor;
}
public static void Main(string[] args)
{
int n = 100000;
for(int i = 0; i< 100; i++) testAbstract(n);//warm up
Console.WriteLine(testAbstract(n));
for(int i = 0; i< 100; i++) testInterface(n);//warm up
Console.WriteLine(testInterface(n));
}
}
}

FastCode
یک شنبه 12 مهر 1394, 18:44 عصر
تستتون غلطه
اگر در کلاسهای تعریف شده چیزی اضافه کنید میبینید که نتیجه ثابته
در ضمن تعدادی از اون بایتهایی که بنده ازش صحبت کردم با این تست نشان داده نمیشن چون فقط یک بار در حافظه appdomain برای Runtime Type ها allocate میشن

using System;

namespace memoryTest
{
/**********abstract class************/
abstract class UserBase
{
public abstract void M1 ();

public abstract void M2 ();

public abstract void M3 ();
}

abstract class User : UserBase
{
public abstract void M4 ();
}

class CAbstract1 : User
{

public override void M1 ()
{
}

public override void M2 ()
{
}

public override void M3 ()
{
}

public override void M4 ()
{
}
}

class CAbstract4 : UserBase
{
public override void M1 ()
{
}

public override void M2 ()
{
}

public override void M3 ()
{
}
}
/**********interface ************/
interface IUserBase
{
void M1 ();

void M2 ();

void M3 ();
}

interface IUser
{
void M4 ();
}

class CInterface1 : IUserBase, IUser
{
public void M1 ()
{
}

public void M2 ()
{
}

public void M3 ()
{
}

public void M4 ()
{
}
}

class CInterface4 : IUserBase
{
public void M1 ()
{
}

public void M2 ()
{
}

public void M3 ()
{
}
}


public class Program
{
public static long testInterface (int n)
{
long before = 0, after = 0;
CInterface1[] c1 = new CInterface1[n];
CInterface4[] c4 = new CInterface4[n];
before = GC.GetTotalMemory (true);
for (int i = 0; i < n; i++) {
c1 [i] = new CInterface1 ();
c4 [i] = new CInterface4 ();
}
after = GC.GetTotalMemory (true);
GC.KeepAlive (c1);
GC.KeepAlive (c4);
return after - before;
}

public static long testAbstract (int n)
{
long before = 0, after = 0;
CAbstract1[] c1 = new CAbstract1[n];
CAbstract4[] c4 = new CAbstract4[n];
before = GC.GetTotalMemory (true);
for (int i = 0; i < n; i++) {
c1 [i] = new CAbstract1 ();
c4 [i] = new CAbstract4 ();
}
after = GC.GetTotalMemory (true);
GC.KeepAlive (c1);
GC.KeepAlive (c4);
return after - before;
}

public static void Main (string[] args)
{
int n = 1048576;
for (int i = 0; i < 10; i++)
testAbstract (n);//warm up
Console.WriteLine ((testAbstract (n) + 1048575) / 1048576);
for (int i = 0; i < 10; i++)
testInterface (n);//warm up
Console.WriteLine ((testInterface (n) + 1048575) / 1048576);
}
}
}

plus
یک شنبه 12 مهر 1394, 19:04 عصر
خب؟ مگه مشق دانشگاه بوده؟
16 بایت حافظه + یک lookup + یک entry در vtable
این روش تمیزترین کاربری رو داره

مساله حافظه و سرعت اجرا و ... نیست، افزودن یک لایه وراثت برای مشق دانشگاه و یا از دید تئوری مشکلی نداره ولی در عمل پیچیدگی رو افزایش میده و برای یک چنین مساله ای لزوما بهترین انتخاب نیست. هرچند که صورت مساله هم خیلی ایده آل نیست ولی در عمل چنین حالت هایی پیش میاد.

rahnema1
یک شنبه 12 مهر 1394, 21:16 عصر
تستتون غلطه
اگر در کلاسهای تعریف شده چیزی اضافه کنید میبینید که نتیجه ثابته
در ضمن تعدادی از اون بایتهایی که بنده ازش صحبت کردم با این تست نشان داده نمیشن چون فقط یک بار در حافظه appdomain برای Runtime Type ها allocate میشن

من که از همون اول از WorkingSet64 استفاده کردم که علاوه بر GC چیزهای دیگه را هم نشون میده
در کلاسهای تعریف شده اون چیزی که اضافه می شه باید مقداری که اشغال می کنه از یه حد بیشتر باشه مثلا آرایه استفاده بشه تا نتایج تغییر کنه
در هر صورت اینها را امتحان کردم در مورد هر دو روش مقدار حافظه مورد نظر یکسان بود
حالا اگه روشی مد نظر دارید که جواب بهتری می ده لطفا ارائه بدید

ASKaffash
دوشنبه 13 مهر 1394, 07:07 صبح
سلام
اگریک مهندسی معکوس سطحی در کلاسهای دان نت انجام دهید خواهید دید که عمدتا" از Interface استفاده میگردد وخیلی خبری از کلاسهای abstract نیست :

SabaSabouhi
دوشنبه 13 مهر 1394, 08:19 صبح
سلام
دوستان مطالب خوبی تو این تاپیک نوشتن، اما به نظر من که خیلی نگران چند بایت اختلاف مصرف حافظه یا چند میلی ثانیه اختلاف زمان
اجرا نباشین، واقعاً تو نتیجه تاثیر قابل ملاحظه‌ای نداره. abstract و interface هر کدوم برای کار خاصی طراحی شدن، اگه هر کدوم رو سر
جای خودش مصرف کنین نتیجه خوانایی بیشتر برنامه خواهد بود. همیشه فرض رو بر این بگیرین که یه روزی یه برنامه‌نویس دیگه می‌خواد
این کار رو ادامه بده.

صبا صبوحی

FastCode
دوشنبه 13 مهر 1394, 21:00 عصر
abstract و interface هر کدوم برای کار خاصی طراحی شدن،
this 100 times

ASKaffash
سه شنبه 14 مهر 1394, 06:55 صبح
سلام
ممنون ولی یک موضوع مهم وجود دارد : درست است که در مواردی در پیاده سازی کلاسها به نظر میرسد که هم می شود از abstract استفاده کرد و هم از interface ولی فلسفه اصلی interface در دات نت و جاوا حذف پیچیدگی وراثت چند گانه است وبرای این کار بجای C++ که یک کلاس می تواند از چند کلاس ارث بری کند در اینجا یک کلاس فقط از یک کلاس و بی نهایت اینترفیس ارث بری می کند این یعنی فرق بین وراثت در سطح با وراثت در عمق و همان بحثی که در ابتدای تاپیک گفتم که سطح وراثت افزایش می یابد شاید در این مثال ساده خیلی موضوع مهم نباشد ولی در پروژه های عریض و طویل مهم است

FastCode
سه شنبه 14 مهر 1394, 15:47 عصر
درسته. تا اونجایی که بنده اطلاع دارم و همیشه در پروژه های استفاده کردم وظیفه اصلی هر کلاس معمولا با کلاس abstract مشخص میشه.
شما interfaceی به اسم IStream و IException ندارید. چون از نظر منطقی غیر ممکنه که یه چیزی هم exception باشه و هم stream.
و فکر نمیکنم در مثالی که داریم چیزی هم بتونه کاربر باشه و هم یه چیز دیگه.
interface ها نشون دهنده وضیفه اصلی نیستند.
هر چیزی میتونی Dispose بشه بنابراین IDisposable یک interface هست.ولی هر چیزی نمیتونه یک کاربر باشه.

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

rahnema1
سه شنبه 14 مهر 1394, 22:43 عصر
البته اینکه از عنوان کاربر استفاده شده دلیلی نیست که لزوما کلاسها به صورت تودرتو باشه. چون ملاک تفکیک اینترفیس ها متدها یا کارهایی هست که اونها انجام می دهند
مثلا دو تا اینترفیس ایجاد بشه به نام اینترفیس «برنامه نویس» و اینترفیس «طراح گرافیک»
مثلا از روی این دو تا اینترفیس می شه سه تا کلاس کاربر ایجاد کرد
1 برنامه نویس
2 طراح گرافیک
3 برنامه نویس و طراح گرافیک
در هر صورت این به عنوان مثال بود و باید مساله اصلی چه بوده که بسته به اون بهترین گزینه انتخاب بشه
با استفاده از WinDbg سایز و خصوصیات اشیاء را در آوردم واسه سیستم 32 بیتی:


Class Name
Vtable Slots
Total Method Slots
Object Size


CAbstract1
8
9
12


CAbstract4
7
8
12


CInterface1
8
9
12


CInterface4
7
8
12

ASKaffash
چهارشنبه 15 مهر 1394, 12:08 عصر
سلام
همانطور که میدانید تشخیص استفاده از یک Member در یک کلاس abstract باشد یا در یک Interface باشد یا Virtual باشد واقعا به کاربرد آن در موضوع و تشخیص معمار نرم افزار دارد هرسه مفهوم در مقوله پلی مورفیسم است ولی یک چیز کاملا قابل تشخیص است هر وقت یک member در یک کلاس حتما باید یک پیاده سازی اولیه داشته باشد استفاده از Virtual یک الزام است از طرفی Access Modifier در یک اینترفیس به معمار تحمیل می گردد که ممکن است در مواردی اجبارا" به سمت member از نوع abstract سوئیچ کنیم