الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

تمرين على استخدام القوائم (باستخدام ++C )

بدأه C++er في 17 نوفمبر 2013 · 27 رد · 3,631 مشاهدة · في مشاريع ومسابقات القسم
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

اقرأ مقدمة الموضوع

يبدو انى وصلت متأخر، هذا هو اقتراحي:

 

1- المشكلة

وجود مجموعة من المواضيع بتفاصيل محدده و التى لابد من ادخالها بأى ترتيب و بعدها امكانية عرضها بترتيب معين و فى مدى محدد.

 

*------------------------------------------*

 

1.1- المتطلبات:

 

عناصر الموضوع:

A- عنوان الموضوع.

B- اسم الكاتب.

C- تاريخ الكتابة بالشكل day/month/year.

D- عدد مرات المشاهدة.

E- عدد الردود.

F- رابط الموضوع.

 

خصائص الترتيب:

A- الترتيب قد يكون تصاعدى أو تنازلي.

B- الترتيب عن طريق اى بيان مدخل.

C- الترتيب قد يتم تجاهله و يتم إستخدام الترتيب الذى تم الإدخال به.

 

خصائص العرض:

A- يتم تحديد رقم الموضوع بإستخدام خصائص الترتيب المحدده.

B- يتم تحديد عدد معين من المواضيع بإستخدام خصائص الترتيب المحدده.

 

*------------------------------------------*

 

2- التحليل

 

* حيث أن كل عناصر الموضوع يمكن الترتيب بها حينها لابد من إيجاد قيمة رقمية تمثل كل منهم لتسهيل البحث و الترتيب:

A- عنوان الموضوع: سيتم إستخدام عدد الحروف به بالإضافة لـ hash يمثل محتوى النص و عند الترتيب سيتم إستخدام عدد الحروف يليها الـ hash.

B- اسم الكاتب: كما فى عنوان الموضوع.

C- تاريخ الكتابة: سيتم إدخاله كنص و التعامل معه كـ uint حيث اليوم سيحفظ فى بايت و الشهر فى بايت و السنه فى 2 بايت و سيتم دمج القيم معا فى 4 بايت لتستخدم فى الترتيب.

D- عدد مرات المشاهدة: رقم صحيح موجب.

E- عدد الردود: كما فى عدد مرات المشاهدة.

F- رابط الموضوع: كما فى عنوان الموضوع.

 

* حيث ان ترتيب الإدخال الأولي مطلوب سيتم حفظ المحتويات داخل list و ستسمي data store و من خلالها نستطيع الإضافة و الحفظ و التعديل.

* للترتيب سيتم عمل 3 فئات كل منهم تمثل نوع ترتيب معين و بداخل أى منهم سيوجد list و كل عنصر فيه هو رقم الموضوع داخل data store بعد ان تقوم فئة الترتيب بتطبيق نمط عملها.

* للعرض سيوجد فئتين كل منهم سيتم إدخال لها نوع الترتيب و الإفتراضي هو ترتيب الإدخال.

 

*-----------------------------------------*

 

عموما التحليل الأولي دائما ما يحتوى على نواقص و اخطاء و هذا يظهر داخل التصميم الاولي و بعدها نعود للتحليل و نقوم بتحسينه و نعود لنطبق داخل التصميم و هكذا حتى يصبح التحليل و التصميم أفضل ما يمكن و هنا تبدأ المرحله الرابعه و هي كتابة الكود لجعل التصميم فعال.

 

فى حالتنا هذه سأكتفي بهذا التحليل وسأفترض ان عملية الذهاب و الإياب إلى و من التصميم قد تمت و ذلك لحفظ الوقت و ايضا للتركيز على محتوى المثال بدلا من SE.

 

*-----------------------------------------*

 

سأضيف التصميم بعد قليل.

 

 

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 19 نوفمبر 2013 في 00:47 — السبب: إضافة رابط المقدمة

1

مدونتي: C++ Tips and Tricks

#2

بارك الله فيك أخي C++er .. تحليل منظّم ورائع .. عندي بعض الأسئلة :

اقتباس

عناصر الموضوع:

A- عنوان الموضوع.

B- اسم الكاتب.

C- تاريخ الكتابة بالشكل day/month/year.

D- عدد مرات المشاهدة.

E- عدد الردود.

F- رابط الموضوع.

1- هل يمكننا تصميم الحل بحيث يمكننا تغيير عناصر الموضوع متى أردنا دون أن تتأثر باقي الأجزاء ؟ هذا ما كنا نناقشه قبل وصولك بقليل .

اقتباس

* حيث أن كل عناصر الموضوع يمكن الترتيب بها حينها لابد من إيجاد قيمة رقمية تمثل كل منهم لتسهيل البحث و الترتيب:

A- عنوان الموضوع: سيتم إستخدام عدد الحروف به بالإضافة لـ hash يمثل محتوى النص و عند الترتيب سيتم إستخدام عدد الحروف يليها الـ hash.

2- ألا يمكننا استخدام الترتيب المعجمي lexical sort ؟ وعندها لن نحتاج لتحويل النص إلى قيمة عددية , ولكن أظن أنك قمت بذلك لأنك تضع في ذهنك خطوة أبعد  .. (كما قلت .. لا تريد أن ندخل في حلقة التحليل والتصميم )

اقتباس

C- تاريخ الكتابة: سيتم إدخاله كنص و التعامل معه كـ uint حيث اليوم سيحفظ فى بايت و الشهر فى بايت و السنه فى 2 بايت و سيتم دمج القيم معا فى 4 بايت لتستخدم فى الترتيب.

اليوم أصغر من 32 والشهر أصغر من 16  والسنة أصغر من 2048 أي يمكننا استخدام 5+4+11 بت (20 بت ) ونحتفظ بـ 12 بت الباقية للوقت ( بالدقائق يكون اليوم 1440 دقيقة ) ما رأيك :) طالما انك وصلت إلى هذه التفاصيل .

اقتباس

للترتيب سيتم عمل 3 فئات كل منهم تمثل نوع ترتيب معين و بداخل أى منهم سيوجد list و كل عنصر فيه هو رقم الموضوع داخل data store بعد ان تقوم فئة الترتيب بتطبيق نمط عملها.

Object Oriented Thinking :D يبدو أننا سنبتعد عن C قليلاُ.. أو سيكون هناك نوعان من الــ implementation  لكل Programming paradigm .. هذا جيد جداً .. (طبعا أنا أفضل OO )

اقتباس

فى حالتنا هذه سأكتفي بهذا التحليل وسأفترض ان عملية الذهاب و الإياب إلى و من التصميم قد تمت و ذلك لحفظ الوقت و ايضا للتركيز على محتوى المثال بدلا من SE.

 

هل يمكنك تقسيم العمل علينا نحن الثلاثة ؟ كيف ستقوم بتوزيع العمل ؟ وكيف ستحدد المهام ؟

وشكراً جزيلاً لك .

#3
اقتباس
1- هل يمكننا تصميم الحل بحيث يمكننا تغيير عناصر الموضوع متى أردنا دون أن تتأثر باقي الأجزاء ؟ هذا ما كنا نناقشه قبل وصولك بقليل .

بالتأكيد لا، فأنت لا تستطيع تصميم سيارة براكب واحد ليركبها أربعة، لابد من تعديل التصميم.

 

اقتباس
2- ألا يمكننا استخدام الترتيب المعجمي lexical sort ؟ وعندها لن نحتاج لتحويل النص إلى قيمة عددية , ولكن أظن أنك قمت بذلك لأنك تضع في ذهنك خطوة أبعد  .. (كما قلت .. لا تريد أن ندخل في حلقة التحليل والتصميم )

الفئة الخاصة بالترتيب ستتعامل مع اكثر من عنصر و بالتالي فى كل مره سنحتاج للترتيب بالإسم سنضطر للدخول للنص للبدء فى ترتيبه من جديد و هذا سيأخذ بعض الوقت، لهذا قلت سنأخذ عدد الحروف يليها hash لنص حيث كلما كان عدد الحروف اصغر كلما كانت الكلمه هى الأولي و حيث ان وجود اكثر من نص بنفس عدد الحروف امر محتمل لذا كان الـ hash للترتيب به فكلما كانت قيمة الهاش اكبر كلما كان النص فى اسفل السلسة. هذا الترتيب اشبه بالترتيب المعجمى و لكن بإستخدام الأرقام بدلا من النص.

 

بالتأكيد يمكن الإستغناء عن هذه الخطوه و الإستعانه بـ sort function للتسهيل، فماذا تروا؟

 

اقتباس
اليوم أصغر من 32 والشهر أصغر من 16  والسنة أصغر من 2048 أي يمكننا استخدام 5+4+11 بت (20 بت ) ونحتفظ بـ 12 بت الباقية للوقت ( بالدقائق يكون اليوم 1440 دقيقة ) ما رأيك :) طالما انك وصلت إلى هذه التفاصيل .

لو سنتكلم على مستوى البت فى هذه الحالة يمكننا اختال هذه القيم اكثر من ذلك:

uint16_t year:7;  // [-63, +63] with base year 2000
uint16_t month:4; // [1, 12]
uint16_t day:5; // [1, 31]

لكن الهدف ليس إختزال المساحة لأقل ما يمكن و لكن لجعل التصميم سهل و قابل للفهم و التعديل لاحقا، و العمليات على البايت ايسر كثيرا من العلميات على البت و فى هذه النقطة لا اريد الدخول فى تفاصيل تبعدنا عن الهدف الرئيسي.

 

تصميم التاريخ كالتالي:

union date_t
{
	uint32_t all;
	struct { uint16_t day:8, month:8, year:16; };
};

لاحظ ان الـ layout هنا مخصصه لـ little-endian.

 

اقتباس
هل يمكنك تقسيم العمل علينا نحن الثلاثة ؟ كيف ستقوم بتوزيع العمل ؟ وكيف ستحدد المهام ؟

أولا يتم الإنتهاء من التصميم و يتم التأكد انه فعال - اى لا توجد اخطاء به - و بعدها يعرف كل عضو الفئه التى سيعمل بها و ما المفترض ان تقوم به و بناءا على تصميمها يقوم بإضافة الكود و الذى من شانه جعل الفئه تعمل كما هو متوقع و فى هذا الأثناء يمكنه إضافة private functions لتسهل الأمور له.

 

التصميم الذى سأضعه يحتوى على العديد من الأجزاء التى تستخدم الـ templates لتقليل نسخ الكود و ايضا لوضعه فى مكان واحد. فإذا كانت توجد مشكلة بذلك يمكن العمل على تغييره.

 

سأنتهى منه بعد قليل.

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#4
اقتباس
بالتأكيد يمكن الإستغناء عن هذه الخطوه و الإستعانه بـ sort function للتسهيل، فماذا تروا؟

طالما أن hashing أسرع فهو أفضل :) .. ولا مانع من وجود كلا الطريقتين ..

اقتباس

لكن الهدف ليس إختزال المساحة لأقل ما يمكن

كانت مجرد فكرة :)

اقتباس

التصميم الذى سأضعه يحتوى على العديد من الأجزاء التى تستخدم الـ templates لتقليل نسخ الكود و ايضا لوضعه فى مكان واحد. فإذا كانت توجد مشكلة بذلك يمكن العمل على تغييره.

مهما كانت طريقة التنفيذ فلا مشكلة , فمرحلة التصميم التي تقوم بها تفيدنا جداً .. شكرا لك .

بعد انتهاء التنفيذ في ++C سنقوم بإعادة كتابته في C .. بإذن الله .

 

متشوق للبدء في العمل .:)

#5

2- التحليل (تعديل 1):

اقتباس
* للترتيب سيتم عمل 3 فئات كل منهم تمثل نوع ترتيب معين و بداخل أى منهم سيوجد list و كل عنصر فيه هو رقم الموضوع داخل data store بعد ان تقوم فئة الترتيب بتطبيق نمط عملها.

سيتم تعديل هذه النقطه فبدلا من وجود 3 سيوجد عدد من الفئات يماثل عدد عناصر Subject و سيوجد واحد إضافي وظيفته إستخدام ترتيب الإدخال.

 

3- التصميم:

التالي هو التصميم المبدئي:

 

توجد بعض الأنواع التى سيبنى عليها البرنامج و سيتم وضعهم بالملف types.hpp

typedef unsigned short uint16_t;
typedef unsigned int   uint32_t;

// generic value
template<typename type, type input>
struct value_t
{
	static const type value = input;
};

// true value
struct  true_t : public value_t<bool,  true> {};
// false value
struct false_t : public value_t<bool, false> {};

// date packed information
union date_t
{
	uint32_t all;
	struct { uint16_t day:8, month:8, year:16; };
};

//string hash data
struct hash_t
{
	uint32_t count, hash;
};

// subject elements
struct Subject
{
	string    name;
	string    link;
	string    owner;
	string    date;
	uint16_t  views;
	uint16_t  replies;
};

// sort direction
enum SortDirection
{
	//From smallest to largest. e.g, from A to Z.
	sdAscending,
	//From largest to smallest. e.g, from Z to A.
	sdDescending
};

// id for subject elements
enum ElementID
{
	eNone = -1, // no element specified

	eName = 0, // used for subject.name
	eNameHash, // used for subject.name.hash

	eLink,     // used for subject.link
	eLinkHash, // used for subject.link.hash

	eOwner,     // used for subject.owner
	eOwnerHash, // used for subject.owner.hash

	eDate,     // used for subject.date
	eDateInfo, // used for subject.date.info

	eViews, // used for subject.views

	eReplies // used for subject.replies
};

// get underling-type of subject-element
template<ElementID id> struct get_element_type;
template<> struct get_element_type<eName>      { typedef string   type; };
template<> struct get_element_type<eNameHash>  { typedef hash_t   type; };
template<> struct get_element_type<eLink>      { typedef string   type; };
template<> struct get_element_type<eLinkHash>  { typedef hash_t   type; };
template<> struct get_element_type<eOwner>     { typedef string   type; };
template<> struct get_element_type<eOwnerHash> { typedef hash_t   type; };
template<> struct get_element_type<eDate>      { typedef string   type; };
template<> struct get_element_type<eDateInfo>  { typedef date_t   type; };
template<> struct get_element_type<eViews>     { typedef uint16_t type; };
template<> struct get_element_type<eReplies>   { typedef uint16_t type; };

// is input id is for element that defined in subject class.
// a compile time error will occur for using non-subject-class element.
template<ElementID id> struct public_element : public false_t {};
template<> struct public_element<eName>    : public true_t, public get_element_type<eName>    {};
template<> struct public_element<eLink>    : public true_t, public get_element_type<eLink>    {};
template<> struct public_element<eOwner>   : public true_t, public get_element_type<eOwner>   {};
template<> struct public_element<eDate>    : public true_t, public get_element_type<eDate>    {};
template<> struct public_element<eViews>   : public true_t, public get_element_type<eViews>   {};
template<> struct public_element<eReplies> : public true_t, public get_element_type<eReplies> {};

// is input id is for element that we can order with.
// a compile time error will occur for using element that can not be ordered.
template<ElementID id> struct sort_with_element : public false_t {};
template<> struct sort_with_element<eNone>      : public true_t {};
template<> struct sort_with_element<eNameHash>  : public true_t, public get_element_type<eNameHash>  {};
template<> struct sort_with_element<eLinkHash>  : public true_t, public get_element_type<eLinkHash>  {};
template<> struct sort_with_element<eOwnerHash> : public true_t, public get_element_type<eOwnerHash> {};
template<> struct sort_with_element<eDateInfo>  : public true_t, public get_element_type<eDateInfo>  {};
template<> struct sort_with_element<eViews>     : public true_t, public get_element_type<eViews>     {};
template<> struct sort_with_element<eReplies>   : public true_t, public get_element_type<eReplies>   {};

سيتم إستخدام uint16_t و uint32_t للإشارة إلى النوعين unsigned short و unsigned int على التوالي.

 

النوع value_t يستخدم للإشارة لقيمة بنوع معين و إستخدام هذا النوع محدود كنوع مساعد للأنواع الأخرى.

 

النوع true_t و false_t يمثلا القيم true و false على التوالي.

 

النوع date_t يمثل قيمة التاريخ كقيمه رقميه.

 

النوع hash_t يمثل الـ hash value الخاصه بنص.

 

النوع Subject و يمثل الموضوع و عناصره على التوالي تمثل إسم الموضوع و رابطه و كاتبه و تاريخ كتابته و عدد مشاهداته و عدد الردود به.

 

النوع SortDirection يمثل طريقة ترتيب العناصر و هما اثنان sdAscending و sdDescending و يمثلا تصاعدى و تنازلي على التوالي.

 

النوع ElementID يمثل عناصر النوع Subject و مجموعة اخرين حيث:

   - eNone تستخدم أثناء الترتيب و تعنى إستخدام الترتيب اللإتراضي.

   - eName هو إسم الموضوع و يستخدم فى تعديل إسم الموضوع.

   - eNameHash هو إسم الموضوع و يستخدم فى الترتيب.

   - eLink هو رابط الموضوع و يستخدم فى تعديل رابط الموضوع.

   - eLinkHash هو رابط الموضوع و يستخدم فى الترتيب.

   - eOwner هو إسم كاتب الموضوع و يستخدم فى تعديل إسم كاتب الموضوع.

   - eOwnerHash هو إسم كاتب الموضوع و يستخدم فى الترتيب.

   - eDate هو تاريخ كتابة الموضوع و يستخدم فى تعديل تاريخ كتابة الموضوع.

   - eDateInfo هو تاريخ كتابة الموضوع و يستخدم فى الترتيب.

   - eViews هو عدد مشاهدات الموضوع و يستخدم فى تعديل و الترتيب بإستخدام عدد مشاهدات الموضوع.

   - eReplies هو عدد الردود بالموضوع و يستخدم فى تعديل و الترتيب بإستخدام عدد الردود الموضوع.

 

النوع get_element_type يستقبل قيمه من النوع ElementID و يحتوى على نوع العنصر داخل Subject و الذى يمثله قيمه عنصر ElementID.

 

النوع public_element يستقبل قيمه من النوع ElementID و يعيد لك false إذا كانت القيمه المدخله لا تحتوى على عنصر داخل النوع Subject و تعيد true و النوع نفسه إذا كان العنصر موجود داخل النوع Subject.

 

النوع sort_with_element يستقبل قيمه من النوع ElementID و يعيد لك false إذا كانت القيمه المدخله لا يمكن الترتيب بها و يعيد true و النوع القيمه االتى سيتم الترتيب بها.

 

*---------------------------------------------*

 

يأتى الدور على النوع DataStore و الذى يقوم بحفظ و استرجاع الـ subjects المدخله من المستخدم و هو موجود بالملف data_store.hpp:

class DataStore
{
public:

	inline DataStore() {}

	// get number of subjects
	inline int count() const;

	// insert a subject
	inline bool add(const Subject&);

	// delete input subject
	inline void del(const Subject&);

	// delete subject at input index
	inline void del(int);

	// update subject at input index.
	inline void update(int, const Subject&);

	// update public element
	template<ElementID id>
	inline void update_element(int, const typename public_element<id>::type&);

	// get a copy of subject at input index
	inline const Subject& at(int);

	// get element at input index
	template<ElementID id>
	inline const typename get_element_type<id>::type& get_element_at(int) const;

private:
	struct idata
	{
		Subject s;
		hash_t  n, l, o;
		date_t  d;
	};

	inline void update_name_hash();
	inline void update_link_hash();
	inline void update_owner_hash();
	inline void update_date_info();

private:
	vector<idata> list;
};

الفئه store تحتوى على النوع idata و الذى يحتوى على الـ subject بالإضافة لبعض العناصر المرتبطه به، يمكنك اعتبار هذه العناصر كـ cache لبعض عناصره بدلا من إعادة حسابها اكثر من مره.

 

الدوال update_name_hash و update_link_hash و update_owner_hash يستخدموا لتحديث الـ hash لكل من إسم الموضوع و رابطه و إسم كاتبه على التوالي.

الدالة update_date_info تقوم بتحديث بيانات التاريخ.

 

المتغير list يقوم بحفظ القيم المدخلة من المستخدم و هى قيمه ذات محتوى سليم قد تم التحقق منه (من فئة الإدخال و سيأتى ذكرها لاحقا).

 

الدالة count تقوم بإرجاع عدد المواضيع المحفوظه داخل store.

 

الداله add تقوم بإضافة Subject جديد.

 

الدالة del تقوم بالحذف و توجد منها نسختين احداهما تأخذ الموقع الذى سيتم الحذف عنده و الأخرى ستقوم بحذف object.

 

الدالة update تقوم بتحديث subject موجود موقع معين.

 

الدالة update_element تقوم بتحديث عنصر من داخل subject و بموقع معين داخل الـ store. لاحظ ان هذه الدالة template و تقبل فقط تحديث قيمة احد عناصر النوع subject.

 

الدالة at تقوم بإرجاع subject عند الموقع المدخل.

 

الدالة get_element_at تقوم بإرجاع عنصر من داخل النوع idata.

 

*---------------------------------------------*

 

الأن لدينا مخزن البيانات ناتى لفئة الترتيب و هى موجودة بالملف sort.hpp

// used to sort subjects by element
template<ElementID id, bool can_order = sort_with_element<id>::value>
class Sort;

template<ElementID id>
class Sort<id, true>
{
public:
	inline Sort<id, true>(const DataStore& Store);
	inline Sort<id, true>(const DataStore& Store, SortDirection Dir);

	inline bool sorted() const;

	inline SortDirection get_dir() const;

	inline const DataStore& get_store() const;

	inline void sort();

	inline void resort();

	inline const Subject& get_subject(int) const;

private:
	inline void sort_none();
	inline void sort_name();
	inline void sort_link();
	inline void sort_owner();
	inline void sort_date();
	inline void sort_views();
	inline void sort_replies();

private:
	bool             is_sorted;
	SortDirection    dir;
	const DataStore& store;
	vector<uint32_t> list;
};

الفئة sort تقبل فقط الترتيب بالعناصر المسموح الترتيب بها فمثلا انت لا يمكنك الترتيب بالعنصر Name و لكن يمكن الترتيب بالعنصر NameHash.

 

الفئه sort لإنشائها تحتاج لتمرير مخزن بيانات ليتم التعامل معه و إتجاه للترتيب فإن لم تختاره سيكون الإفتراضي هو تصاعدى.

 

العنصر is_sorted  قيمته true إذا تم الترتيب، false إن لم يتم الترتيب.

العنصر dir هو إتجاه الترتيب.

العنصر store هو مخزن البيانات.

العنصر list يحتوى على subject index بعد الترتيب.

 

الدالة sorted تعيد قيمة is_sorted.

الدالة get_dir تعيد قيمة dir.

الدالة get_store تعيد قيمة store.

الدالة sort تقوم بعملية الترتيب إذا كانت قيمه is_sorted تساوى false.

الدالة resort تقوم بإجبار عملية الترتيب حتى و إن كانت المحتويات مرتبه. (يمكنك التفكير كأنها تقوم بعمل refresh لمحتوياتها إذا تم إضافة او حذف subject من store).

الدالة get_subject تقوم بإرجاع subject موجود عند الموقع المدخل.

 

الدوال *_sort يقومموا بعملية الترتيب بناءا على الـ ElementID ألمدخل و توجد دالة لكل عنصر يمكن الترتيب به. هذه الدوال هى من يقوم بعملية الترتيب الفعلي.

 

*---------------------------------------------*

 

بعد ان قمنا بحفظ البيانات داخل الـ store و قمنا بتحديد وسيلة الترتيب بقي تحديد وسيلة العرض و هى موجود بالملف view.hpp

template<typename sort_t>
class View
{
public:
	inline View<sort_t>(const sort_t& Sort);

	inline void print(int count);

private:
	inline void print_subject(int index) const;

private:
	const sort_t& sort;
};

الفئه view تطلب منك فئة الترتيب و التى من خلالها سنقوم بالعرض.

 

العنصر sort هو فئة الترتيب.

 

الدالة print_subject تقوم بطباعة subject موجود داخل موقع معين.

 

الدالة print تقوم بطباعة عدد معين من subjects.

 

*---------------------------------------------*

 

الفئه الأخيره هي Forum و موجوده بالملف forum.hpp

class Forum
{
public:
	inline Forum();

	// start input operation
	inline void start();

private:
	inline void add_subject();

	inline void update_subject();

	inline void del_subject();

	inline void sort_data();

	inline void view_data();

	inline void print_menu(int);

private:
	DataStore store;
};

الفئه Forum هى الفئه التى يقوم المستخدم بالتعامل معها حيث من خلالها تظهر القوائم و يتم إخال الأوامر منها.

 

العنصر store هو مخزن البيانات.

 

الدالة start تقوم بعرض القائمه الرئيسية تمهيدا لإدخال البيانات و معالجتها و بنهاية هذه الدالة يصبح البرنامج جاهز للإنهاء.

 

الدوال التالية تقوم بإدارة القوائم تبعا لحالة العمليه حيث:

 

الدالة add_subject تقوم بإضافة subject جديد.

الدالة update_subject تقوم بتعديل محتوى subject معين.

الدالة del_subject تقوم بحذف subject معين.

الدالة sort_data تقوم بترتيب البيانات.

الدالة view_data تقوم بإظهار البيانات.

الدالة print_menu تقوم بإظهار قائمه معينه.

 

*---------------------------------------------*

 

و أخيرا الدالة main موجوده داخل الملف main.cpp و الذى يقوم بإدارة كافة الملفات السابقة:

#include <string>
#include <vector>
#include <iostream>
using std::string;
using std::vector;
using std::cin;
using std::cout;

#include "types.hpp"
#include "data_store.hpp"
#include "sort.hpp"
#include "view.hpp"
#include "forum.hpp"


int main()
{
}

لا حظ انه حتى الأن هذا مجرد تصميم فإن قمت بترجمة الملف main.cpp فسيترجم معك بدون مشاكل و لكن إن حاولت إستخدام اى من الفئات المعرفة فيظهر لك اخطاء كثيرة لعدم وجود اى كود مكتوب و الذى سيأتي المرحلة القادمة.

 

الكود بالمرفقات

design.zip

 

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة C++er في 17 نوفمبر 2013 في 23:47

مدونتي: C++ Tips and Tricks

#6

رائع تصميم متقن .. ولكن عندي بعض الأسئلة إن سمحت :

1- بداخل الملف types.hpp أرى أن الكثير من التحسينات من المكن القيام بها :

كلها تتمحور حول :

// a compile time error will occur for using non-subject-class element.
template<ElementID id> struct public_element : public false_t {};
template<> struct public_element<eName> : public true_t, public get_element_type<eName> {};

1.1- لماذا استخدمنا النوع false_t و true_t حيث لم نستخدم القيمة value أبداً ؟

1.2- ألا يمكننا تحقيق الـcompile error باستخدام الأسلوب التالي :

template<ElementID id> struct public_element ;
template<> struct public_element<eName> : public get_element_type<eName> {};

1.3 - لم أفهم فائدة public_element  ! وأين سنحتاجها ؟

1.4- لماذا نستخدم enum  ثم نستخدم get_element_type أليس هذا إطالة للطريق لماذا لا نكتب typedef لكل نوع كما يلي :

typedef void eNone     ;  // no element specified

typedef string eName ;    // used for subject.name
typedef string eLink ;    // used for subject.link
typedef string eOwner ;   // used for subject.owner
typedef string eDate ;    // used for subject.date

typedef hash_t eLinkHash ;// used for subject.link.hash
typedef hash_t eNameHash ;// used for subject.name.hash
typedef hash_t eOwnerHash;// used for subject.owner.hash

typedef date_t eDateInfo; // used for subject.date.info
typedef uint16_t eViews;  // used for subject.views
typedef uint16_t eReplies // used for subject.replies

وبهذا سنوفّر علينا الطريق .. حيث أننا كلما استعملنا idالنوع سنستخدم بعده get_element_type ...

ألا توافقني ؟

1.5- أخيرا يمكننا كتابة subject كما يلي (بعد تنفيذ التعديلات السابقة )

struct Subject
{
    eName  name;
    eLink    link;
    eOwner    owner;
    eDate    date;
    eViews  views;
    eReplies  replies;
};

2- بالنسبة لـ

اقتباس

الدوال *_sort يقومموا بعملية الترتيب بناءا على الـ ElementID ألمدخل و توجد دالة لكل عنصر يمكن الترتيب به. هذه الدوال هى من يقوم بعملية الترتيب الفعلي.



لماذا نكتب دالة لكل نوع ترتيب ؟ ألا يمكننا كتابة template حيث أن لب الترتيب نفسه .. فأما يكون لدينا string ستتحول إلى hash_t او يكون لدينا uint16_t

مثلاً .. سيكون لب دالة الترتيب التي كتبتها لـdate فرضا كما يلي

foreach (date i in list)
{
    date tmp = findmax(from i to end)
    swap ( i , tmp )
}

ويمكن بسهولة تحويلها إلى template  كما يلي:

template <typename T>
...
foreach (T i in list)
{
    T tmp = findmax<T>(from i to end)
    swap<T> ( i , tmp )
}

قرأت مرة أن جزءاً من التصميم يكون بكتابة كود اختبار الدوال ( بدون تنفيذه طبعاً ) وهذا يقرّب ويوضّح طريقة استخدام الدوال التي تم تصميمها ..

فهل مثلاً الاستخدام التالي صحيح :

داخل main الإنشاء والبدء فقط

Forum ArabTeam2000;    ArabTeam2000.start();

داخل start مخاطبة المستخدم واستدعاء دوال Forum

inline void start(){
        //asking user for choice
        switch(choice)
        {
            case add:add_subject();
            ...
        }
    }

أظن أن هذه المرحلة هامة لتبيان أي نقص في التصميم .. فمثلاً من الكود السابق يظهر أننا بحاجة إلى enum لخيارات المستخدم مثلاً ..

قمت بتنفيذ التعديلات التي ذكرتها في 1 و 2 ووضعتها في المرفقات ..

وشكرا جزيلاً لك .

أخيراً عندي سؤال : لقد وضعنا المكاتب كلها في main ولكن أليس من المفترض أن نضع vector مثلاً في الملف الذي يستخدم vector وهكذا .. حتى cin و cout لن نستخدمها في main فلماذا لا نضيفها في الملفات التي تستخدمها ؟

 

Forum.rar

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 18 نوفمبر 2013 في 14:44

#7
اقتباس
1.1- لماذا استخدمنا النوع false_t و true_t حيث لم نستخدم القيمة value أبداً ؟

فى التصميم قبل هذا (لم أضعه) كان يوجد إحتياج لهم داخل النوعين public_element و sort_with_element و مع التحسينات التى اجريتها لم يعد لهم حاجه داخل النوع public_element و لكنى نسيت تحديث هذا النوع.

 

اقتباس
1.2- ألا يمكننا تحقيق الـcompile error باستخدام الأسلوب التالي :

هذا هو الشكل المفترض ان يكون عليه النوع public_element.

 

اقتباس
1.3 - لم أفهم فائدة public_element  ! وأين سنحتاجها ؟

داخل الفئه DataStore توجد هذه الدالة:

template<ElementID id>
inline void update_element(int, const typename public_element<id>::type&);

فلنتصور ان تعريفها كالتالي:

template<ElementID id>
inline void update_element(int, const typename get_element_type<id>::type&);

بالتعريف الجديد يمكنك تحديث قيمة اى عنصر سواء كان موجود فى subject او موجود داخل idata و هذا خطأ حيث المفترض ان المستخدم يعدل محتويات subject فقط لذا قمت بكتابة public_element و التى وظيفتها ان تعيد لك انواع العناصر الموجوده داخل subject فقط و بالتالي عند استخدامك لـ update_element يمكنك فقط تحديد العناصر التى داخلها و غير ذلك سيحدث خطأ وقت الترجمه.

 

ما لا تعرفه ان الدالة update_element ستسبب مشكله إن قمت بكتابة الكود لها بشكل مباشر لأنها ستعيد اكثر من نوع، و هذه المشكله لها حل سنتطرق له وقت كتابة الكود.

 

اقتباس
1.4- لماذا نستخدم enum  ثم نستخدم get_element_type أليس هذا إطالة للطريق لماذا لا نكتب typedef لكل نوع كما يلي :

نعم يمكن و ستختصر الطريق و عيبها هو انك لا تستطيع مقارنة اسم نوع بأخر (إلا بإستخدام RTTI و هذا وقت runtime) فمثلا لا استطيع استخدام جملة switch بإسم النوع، و هذا ما سنقوم به داخل فئة الترتيب.

ايضا يعيبها المنطق، فمثلا لنفترض انى اريد تنفيذ حلقة تكرار فى حالة الإسم و جملة شرطية فى حالة عدد المشاهدات، فكيف لى ان اعرف طبيعة المدخل إذا كان type و ليس value.

 

اقتباس
لماذا نكتب دالة لكل نوع ترتيب ؟ ألا يمكننا كتابة template حيث أن لب الترتيب نفسه .. فأما يكون لدينا string ستتحول إلى hash_t او يكون لدينا uint16_t

لاحظ انا لم اقل دالة لكل نوع ترتيب و لكن قلت دالة لكل عنصر ليتم الترتيب به. و ما تقول صحيح و هو ما اريد و لكني نسيت وضعها بالكود (أعذرنى فالوقت الذى اخذته بالتحليل و التصميم كان ساعتين).

 

الدوال sort_* ستقوم بعمل redirection على دوال الترتيب الفعليه و سبب وجودهم هو انى قد الغى فكرة الترتيب لأى منهم و اقوم بتضمينها بشكل مختلف تماما و من هنا الغي استدعاء دالة الترتيب و اكتب الكود فى الدالة مباشرة.

 

اقتباس
قرأت مرة أن جزءاً من التصميم يكون بكتابة كود اختبار الدوال ( بدون تنفيذه طبعاً ) وهذا يقرّب ويوضّح طريقة استخدام الدوال التي تم تصميمها ..

تسمي Write Tests First و هى احد مفاهيم  Extreme Programming فى مرحلة الـ coding. انا استخدم Prototype Model قليلا.

 

اقتباس
 فهل مثلاً الاستخدام التالي صحيح :

  داخل main الإنشاء والبدء فقط

 داخل start مخاطبة المستخدم واستدعاء دوال Forum

على حسب مقوعك فى سلسة تطوير البرنامج تنظر للأمور بشكل مختلف.

 

إذا كنت Leader ستحتاج لمعرفة كل اجزاء المشروع لأنك فى النهاية ستقوم بدمج هذه الأجزاء معا.

إذا كنت coder فكل ما يهمك هو معرفة حدود الجزء الذى ستعمل به و تعرف مدخلاته و مخرجاته و ما الوظيفه التى سيقوم بها.

 

طبعا كلما كبر حجم المشروع كلما زادت الأقسام داخله و بالتبعيه زاد تعقيده (إداريا).

 

بالنسبه لمثالثك فهو صحيح و هذا هو الشكل النهائي المفترض ان يكون عليه البرنامج.

 

اقتباس
أخيراً عندي سؤال : لقد وضعنا المكاتب كلها في main ولكن أليس من المفترض أن نضع vector مثلاً في الملف الذي يستخدم vector وهكذا .. حتى cin و cout لن نستخدمها في main فلماذا لا نضيفها في الملفات التي تستخدمها ؟

لأسهل على نفسي و تذكر اننا لم نكتب كود كل هذا تصميم مجرد.

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#8

حسناً أنت الـLeader

وأنا coder .. ما هي المهام التي ستسندها إلي ؟ أظن أنني فهمت الأفكار التي طرحتها تماماً .. والطريقة التي ترغب بالعمل بها ..

أعطني التوجيهات فقط وسأبدأ بالعمل بمقتضاها بإذن الله )

 

بانتظارك(وشكراً لوقتك :))

#9

قمت بإجراء بعض التعديلات على التصميم، و وضعته داخل github فى المسار https://github.com/muhammad1985/list

 

اولا: سجل فى موقع github ثم اعطنى اسم المستخدم الخاص بك حتى اسجلك فى المشروع.

 

ثانيا: سأفترض انك لم تستخدم git من قبل، ستحتاج لتحميل ادوات git، بعدها قبل تنفيذ الخطوات الموجوده بالكود اقرء ما اسفلها:

1- git clone https://github.com/muhammad1985/list.git
2- git commit -m "message"
3- git push
4- git pull

الخطوه 1 ستقوم بإنشاء مجلد بالإسم list و بداخله نسخة من الملفات على الموقع و ايضا private repo حتى تقوم بحفظ التعديلات داخلها تمهيدا لنقلها بالموقع.

الخطوه 2 تقوم بتنفيذها لحفظ التعديلات التى قمت بها داخل الـ private repo الموجود لديك و الكمله message هى رسالة وصفية للمحتوى المعدل.

الخطوه 3 تقوم بنقل التعديلات اتى تم حفظها داخل private repo إلى الموقع.

الخطوه 4 تقوم بها حتى تأتى التحديثات الموجوده على الموقع فى نسخة الكود لديك.

 

الخطوات 2 و 3 و 4 ستحتاج لأنك تكون داخل المجلد list.

 

قمت بكتابة الأكواد الأساسية المسئولة عن جعل البرنامج يبدء بالعمل و وضعت الدوال المراد تضمينها بملفات منفصلة حتى لا يحدث إختلاط بين الأكواد الأساسية و الأكواد التى سنقوم بكتابتها.

الملفات التى سنكتب بها الكود تنتهى بـ imp.inl_* و كذلك الملف forum.inl.

إذا اردت إضافة دوال جديده اجعلها private و ضعها فى section قبل المتغيرات الموجوده فى اخر الفئة و قم بكتابة الكود لها داخل ملفات imp.inl او forum.inl.

قم بمراجعة الكود المكتوب و التصميم و إن كان لك استفسارات او تعديلات لنتكلم عنها قبل توزيع المهام بيني و بينك.

 

المترجمات التى سنعمل عليها هى:

gcc 4.8.0clang 3.3.0visual c++ 2008 or cl 15.0intel c++ 13.0

اختر ايهم فما احدث. النسخ التى لدى هي:

gcc 4.8.1clang 3.3.0vc++ 2008sp1 or cl 15.0intel c++ 14.0

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة C++er في 20 نوفمبر 2013 في 00:08

مدونتي: C++ Tips and Tricks

#10
اقتباس
قمت بكتابة الأكواد الأساسية المسئولة عن جعل البرنامج يبدء بالعمل و وضعت الدوال المراد تضمينها بملفات منفصلة حتى لا يحدث إختلاط بين الأكواد الأساسية و الأكواد التى سنقوم بكتابتها.

رأيتها .. منظمة بشكل ممتاز
اقتباس
الملفات التى سنكتب بها الكود تنتهى بـ imp.inl_* و كذلك الملف forum.inl.

إذا اردت إضافة دوال جديده اجعلها private و ضعها فى section قبل المتغيرات الموجوده فى اخر الفئة و قم بكتابة الكود لها داخل ملفات imp.inl او forum.inl.

لن أضيف دوال جديدة .. سأترك ذلك للLeader
اقتباس
المترجمات التى سنعمل عليها

جميع نسخي قديمة !
ربما هذه هي الأحدث .
Microsoft Visual C++ 2008   91909-152-0000052-60415
وهذه
C:\Users\Mostafa36a2>gcc --version
egcs-2.91.57
أظن انني بحاجة إلى تحديث النسخ ..
اقتباس
قم بمراجعة الكود المكتوب و التصميم و إن كان لك استفسارات او تعديلات لنتكلم عنها قبل توزيع المهام بيني و بينك.

1- أليس من الأفضل أن struct idata تكون class ونضيف إليه الدوال الخاصة به بدلا من أن تكون داخلdata_store ?
2- أرى أن الفئة Sort تملك مرجعاً لـDataStore من يتحكم في كائنات Sort ?
رأيت أنك جعلت View هي من ينشئ كائنات Sort ... على كل حال لا بأس من تركها هكذا .. ولكن قد نحتاج لتعديلها لاحقاً
هناك بعض التصورات الأخرى حول View مثل كونها متعلقة بالـindex وهذا غير محبب .. كنت أتمنى أن تكون متعلقة بكائنات Subjectو هناك View متعلقة بكائنات DataStore ولكن لا بأس فيها كما هي ..
3-أظن أن علاقات الـcomposition والـ friend بحاجة إلى توضيح بشكل عام .
4- هل نقوم بعمل فئة خاصة بمخاطبة المستخدم ؟ أم سنترك الحديث داخل دوال Forum ?
5- نحتاج إلى طريقة إدخال وإخراج لـ DataStore بحيث تتيح التعامل اليدوي من داخل الكود ومخاطبة المستخدم
6- ملاحظة هامة : البنية Subject تحتاج إلى التطوير بشكل أكبر بكثير .. وأظن أنها يجب أن تكون فئة وتحوي دوال التعديل والإدخال الخاصة بها ..
7- دوال DataStore ! هل تتعامل مع Subject أم مع idata ??
8- لم أعرف كيف أستخدم View :p
أهم التعديلات اللازمة برأيي :
Subject يجب أن تكون فئة وتهتم بعمليات الـ I/O الخاصة بها ..
idata يجب أن تكون فئة وتأخذ في بانيها كائن من Subject ..

على كل حال , جميع الملاحظات تظهر تلقائيّاً أثناء كتابة الـimplementation 

في المرفقات قمت بإضافة بعض الأجسام لتتوضح أهمية التعديلات .

#11
اقتباس
1- أليس من الأفضل أن struct idata تكون class ونضيف إليه الدوال الخاصة به بدلا من أن تكون داخلdata_store ?

نعم، هذا صحيح.

 

اقتباس
2- أرى أن الفئة Sort تملك مرجعاً لـDataStore من يتحكم في كائنات Sort ?

تقوم بإنشائهم من داخل Forum ومن ثم تمريرهم للفئه View حتى يمكن العرض بالترتيب المدخل.

 

اقتباس
رأيت أنك جعلت View هي من ينشئ كائنات Sort ... على كل حال لا بأس من تركها هكذا .. ولكن قد نحتاج لتعديلها لاحقاً

لا، View لا تنشي Sort هى فقط تأخذ مرجع لواحد موجود و حتى هى لا تعدل فيه، هى فقط تحصل على Subject بالترتيب الموجود داخل Sort.

 

اقتباس
هناك بعض التصورات الأخرى حول View مثل كونها متعلقة بالـindex وهذا غير محبب ..

هذا كان شرط اساسي فى السؤال الرئيسي:

اقتباس
4- وبعد ذلك يمكنه طلب عرض الموضوع رقم  X الترتيب ... أو عرض أول 30 موضوعاً ,, ( عرض الموضوع يكون بطباعة كامل بياناته )

كما ترى السؤال يطلب عرض الموضوع بموقع محدد او أول عدد معين من المواضيع. و بالتالي لابد ان تكون View مرتبطة بـ Index محدد.

 

اقتباس
كنت أتمنى أن تكون متعلقة بكائنات Subjectو هناك View متعلقة بكائنات DataStore ولكن لا بأس فيها كما هي ..

أولا View لأا علاقة لها بـ DataStore لا من قريب او من بعيد، حيث علاقتها الوحيده هى بـ Sort لأن View تقوم بطباعه موضوع و هذا الموضوع موجود بترتيب معين هذا الترتيب توفره لنا Sort و ليس DataStore.

ثانيا: إرتباط View بشكل مباشر بالـ Subjects سيجعل الترتيب من داخل View و ليس Sort و بهذا سيصبح الكود معقدا كثيرا لأنك سترتب و ستطبع من داخل نفس الفئه.

 

اقتباس
3-أظن أن علاقات الـcomposition والـ friend بحاجة إلى توضيح بشكل عام .

لا توجد Friendship داخل البرنامج.

 

بالنسبة للـ Composition:

1- الفئه Forum هى User Interface و لديها نسخه خاصه بها من DataStore، من خلال Forum نقوم بالتعامل مع المستخدم النهائي و كل البيانات التى يدخلها المستخدم تكون من خلالها و هى من يقوم بإجراء التحقق من صحة المدخلات و بعد تقوم هذه الفئه بتمرير النتيجه إلى الـ DataStore التى انشئتها كمخزن لبياناتها، هذه البيانات قد تكون عبارة عن Subject أو Index للحذف او قيم ليتم إبدال الموجود بها.

 

2- الفئه DataStore تقوم بإدارة كافة الـ Subjects من خلال Data structure داخلي، و وظيفتها هى حفظ و استرجاع البيانات التى تطلب منها، هذه الفئه غير مرتبطه بأى فئة اخرى و لكن كل الفئات الأخرى تعتمد عليها بشكل أو بأخر.

 

3- الفئه Sort تقوم بترتيب محتويات DataStore معين بإستخدام عنصر من الـ Subject و اتجاه ترتيب، و توجد حالة خاصة من فئه Sort حيث لا تقوم بالترتيب نهائيا و تقوم بإرجاع الترتيب الإفتراضي الذى تم إدخال البيانات به و الهدف من هذه الحالة الخاصه هو عدم التواصل مع الفئه DataStore من اى مكان اخر بخلاف Forum.

 

4- الفئه View تقوم بعرض البيانات عن طريق ترتيب معين متمثل بالفئه Sort و التى تمررها للفئه View، لاحظ أن الحالة الخاصة من الفئه Sort التى لا ترتيب إذا كانت غير موجوده كنا سنقوم بكتابة الكود لها هنا و هذا سيكون غير عملي لأن عرض البيانات مرتبط بالترتيب و ليس بمخزن البيانات.

 

اقتباس
4- هل نقوم بعمل فئة خاصة بمخاطبة المستخدم ؟ أم سنترك الحديث داخل دوال Forum ?

Forum تقوم من خلالها بالحصول على طلبات المستخدم و التحقق من صحتها ثم تقوم إماما بتمريرها لـ DataStore او Sort ثم View.

 

اقتباس
5- نحتاج إلى طريقة إدخال وإخراج لـ DataStore بحيث تتيح التعامل اليدوي من داخل الكود ومخاطبة المستخدم

غير صحيح، DataStore هى مخزن مجرد للبيانات لا يقوم بأى تعامل مع المستخدم. تعاملات المستخدم هى وظيفة الفئه Forum و التى تقوم داخليا بحفظ و استرجاع النتائج من داخل DataStore.

 

اقتباس
6- ملاحظة هامة : البنية Subject تحتاج إلى التطوير بشكل أكبر بكثير .. وأظن أنها يجب أن تكون فئة وتحوي دوال التعديل والإدخال الخاصة بها ..

بالنسبة لدوال الإدخال فهذا سيجعل الـ Subject يقوم بالتحقق من مدخلاته و رفضها او قبولها و بهذا لابد من جعل محتويات Subject داخليه و يتم التعامل معها من خلال getter و setter.

بالنسبة لدوال الإخراج فستكون واحده فقط لطباعة المحتويات.

 

دوال التعديل ستتسبب من تغيير تصميم الفئه DataStore حيث سنلغي idata و سنضع المحتويات الخاصه بها داخل Subject ثم نجعلهم داخليين و يتم الحصول عليهم من الـ setter.

ايضا دوال التعديل ستحتاج لتغيير تصميم الفئه Sort.

 

اقتباس
7- دوال DataStore ! هل تتعامل مع Subject أم مع idata ??

كلاهم، حيث تقوم بالحصول على قيم من الاولي او تحديث عنصر معين بها ثم تقوم بتحديث قيمة الـ hash الخاصه به داخل idata.

 

اقتباس
8- لم أعرف كيف أستخدم View :P

من داخل الفئه Forum سيطلب المستخدم طباعة عدد أول 30 موضوع مرتبين بإسم الكاتب، و الكود سيكون كالتالي:

typedef Sort<eOwner> SortByOwner;
SortByOwner sort_by_owner(store);
sort_by_owner.sort();

View<SortByOwner> view(sort_by_owner);
view.print(30); // print first 30 subjects
اقتباس

أهم التعديلات اللازمة برأيي :

Subject يجب أن تكون فئة وتهتم بعمليات الـ I/O الخاصة بها ..

idata يجب أن تكون فئة وتأخذ في بانيها كائن من Subject ..

على كل حال , جميع الملاحظات تظهر تلقائيّاً أثناء كتابة الـimplementation

 

التعديلات تظهر اثناء التصميم و ليس وقت الكود لأنه وقتها ستصبح مشكله كبيرة لأنك غيرك قد بدء فعليا فى البرمجه.

 

على كل من منطق "إذا فعلت شيئا فأفعله بشكل صحيح او لا تقعله"، كل النقاط التى وضعتها تحتاج لإعادة دراسة متانية للتصميم، لذا اعطنى حتى يوم الجمعه و سأعطيك تصميم على اكبر قدر من الجوده بإذن الله :).

 

 

و الله ولي التوفيق.

مدونتي: C++ Tips and Tricks

#12

قمت بإعادة تصميم المشروع من جديد.
 
الفئه subject و تمثل عناصر الموضوع:

// subject information
// subject class assume input data are valid, check them elsewhere.
struct Subject
{
	inline Subject();

	// get/set subject name
	inline void set_name(const std::string&);
	inline const std::string& get_name() const;
	uint32_t get_name_hash() const;

	// get/set subject link
	inline void set_link(const std::string&);
	inline const std::string& get_link() const;
	uint32_t get_link_hash() const;

	// get/set subject owner name
	inline void set_owner(const std::string&);
	inline const std::string& get_owner() const;
	uint32_t get_owner_hash() const;

	// get/set subject creation date
	inline void set_date(const std::string&); // also hash input date
	inline const std::string& get_date() const;
	uint32_t get_date_hash() const;

	// get/set subject views count
	inline void set_views(uint32_t);
	inline uint32_t get_views() const;

	// get/set subject number of replies
	inline void set_replies(uint32_t);
	inline uint32_t get_replies() const;

	// print subject content
	void print() const;

private:
	// create hash for input string
	inline uint32_t hash_string(const std::string&) const;

private:
	std::string name;
	std::string link;
	std::string owner;
	std::string date;
	uint32_t views;
	uint32_t replies;

	uint32_t hash_name;
	uint32_t hash_link;
	uint32_t hash_owner;
	uint32_t hash_date;
};

المتغيرات التى لا تبدأ بـ hash هى العناصر التى يتم إدخالها لـ subject و المتغيرات التى تبدأ بـ hash هى قيم يتم احتسابها داخليا للنسخ النصية.
 
الدوال التى تبدأ بـ set و get يستخدموا للوضع القيمه و الحصول عليها من العنصر المحدد اسمه بالداله.
الدوال التى تنتهي بـ hash تعيد قيمه الـ hash للعنصر المحدد اسمه بالداله.
الدالة print تقوم بطباعة عناصر الـ subject التى تم إدخالها إليه.
الفئه Subject لا تحتوى على دوال للتخاطب مع المستخدم (للحصول على القيم منه) و السبب سأذكره بعد توضيح باقي الفئات.
 
*****************************************************************************
 
الفئه Store و تستخدم لحفظ الـ subject المدخله من المستخدم:

// index out of range exception
struct IndexOutOfRangeException
{
	inline IndexOutOfRangeException(int Index) : index(Index) {};

	inline int get_index() const { return index; }
private:
	int index;
};

struct Store
{
	inline Store();

	// get number of saved elements
	inline int count() const;

	// get element at input index
	// invalid index causes IndexOutOfRangeException
	inline const Subject& at(int) const;

	// add new subject
	inline void add(const Subject&);

	// delete subject at input index
	// invalid index causes IndexOutOfRangeException
	inline void del(int);

	// update content of subject at input index
	// invalid index causes IndexOutOfRangeException
	inline void update(int, const Subject&);

private:
	std::vector<Subject> list;
};

الفئه IndexOutOfRangeException هى exception يحدث عند محاولة استخدام موقع غير موجود بالـ store.
 
المتغير list يحتوى على كافة الـ subject التى تم إدخالها.
الدالة count تعيد عدد العناصر.
الدالة at تعيد subject بموقع يتم إدخاله، الـ IndexOutOfRangeException يحدث إذا كان الموقع المدخل خارج المدى المسموح به.
الدالة add تقوم بإضافة subject.
الدالة del و update يقوما بحذف و تحديث subject بموقع معين، و إذا كان ذلك الموقع خارج المدى المسموح فالـ IndexOutOfRangeException سيتم رميه.
 
*****************************************************************************
 
الفئه sort هى المسئولة عن عمليات الترتيب:

// sort direction
enum Direction
{
	//From smallest to largest. e.g, from A to Z.
	Up,
	//From largest to smallest. e.g, from Z to A.
	Down
};

// subjects not sort exception
struct SubjectsNotSortedException
{
	inline SubjectsNotSortedException() {};
};

// sort data
struct Sort
{
	inline Sort(const Store&);

	// is list sorted
	inline bool sorted() const;

	// get/set sort direction
	inline void set_dir(Direction);
	inline Direction get_dir() const;

	// get data storage
	inline const Store& get_store() const;

	// get element at index,
	// if not sorted SubjectsNotSortedException will throw
	inline const Subject& at(int) const;

	// sort data
	// true if sorted, false if already sorted
	inline bool sort();

	// force resort
	inline void resort();

protected:
	// implement sort algorithm
	virtual void sorting() = 0;

protected:
	Direction        dir;
	const Store&     store;
	std::vector<int> list;

private:
	bool is_sorted;

};

// use default order
struct DefaultSort : public Sort
{
	inline DefaultSort(const Store&);

protected:
	inline void sorting();
};

// sort subjects by name
struct SortByName : public Sort
{
	inline SortByName(const Store&);

protected:
	void sorting();
};

// sort subjects by Link
struct SortByLink : public Sort
{
	inline SortByLink(const Store&);

protected:
	void sorting();
};

// sort subjects by owner
struct SortByOwner : public Sort
{
	inline SortByOwner(const Store&);

protected:
	void sorting();
};

// sort subjects by date
struct SortByDate : public Sort
{
	inline SortByDate(const Store&);

protected:
	void sorting();
};

// sort subjects by views
struct SortByViews : public Sort
{
	inline SortByViews(const Store&);

protected:
	void sorting();
};

// sort subjects by replies
struct SortByReplies : public Sort
{
	inline SortByReplies(const Store&);

protected:
	void sorting();
};

النوع Direction يمثل اتجاه الترتيب.
الفئه SubjectsNotSortedException هى استثناء يتم رميه إذا حاولت الحصول على subject قبل إجراء عملية الترتيب.
 
المتغير is_sorted يحدد هل تمت عملية الترتيب ام لا.
المتغير list يحتوى على مواقع الـ subject داخل store بعد ترتيبها.
المتغير store يحتوى على الـ subject المراد ترتيبها.
المتغير dir هو اتجاه الترتيب.
 
الدالة sorted تعيد قيمة is_sorted.
الدالة set_dir و get_dir يقوما بتحديد و إعادة اتجاه الترتيب.
الدالة get_store تعيد store.
الدالة at تعيد subject بالموقع المدخل، إذا لم تتم عملية الترتيب سيتم رمي الإستثناء SubjectsNotSortedException و إذا كان الموقع المدخل خارج المدى سيتم رمي الإستثناء IndexOutOfRangeException.
الدالة sort تعيد true إذا تمت عملية الترتيب و تعيد false إذا كانت القيم مرتبه بالفعل.
الدالة resort تعيد ترتيب الـ subject حتى و إن كان تم ترتيبهم، الهدف منها هو إذا تم اضافة او حذف او تعديل محتويات الـ store حينها تقوم بإستدعائها لتحدث محتوياتها.
الدالة sorting هى pure function حيث سيتم الوراثة من الفئه sort و يتم كتابة كود لهذه الفئه ليتلائم مع عملية الترتيب المراده.
 
توجد 7 فئات خاصة بالترتيب و هم:
الفئه DefaultSort تمثل الترتيب الإفتراضي الذى تم إدخال البيانات به.
الفئه SortByName تمثل الترتيب بإسم الموضوع.
الفئه SortByLink تمثل الترتيب برابط الموضوع.
الفئه SortByOwner تمثل الترتيب بإسم كاتب الموضوع.
الفئه SortByDate تمثل الترتيب بتاريخ إنشاء الموضوع.
الفئه SortByViews تمثل الترتيب بعدد مشاهدات الموضوع.
الفئه SortByReplies تمثل الترتيب بعدد الردود.
 
كل هذه الفئات تحتوى على الدالة sorting و التى ستقوم فعليا بعملية الترتيب.
 
*****************************************************************************
 
الفئه View تقوم بعرض البيانات:

// abstract class for output sorted data
struct View
{
	inline View(const Sort&);

	inline const Sort& get_sort() const;

	virtual void print(int) const = 0;

protected:
	const Sort& sort;
};

// output sorted subjects by index
struct ViewByIndex : public View
{
	inline ViewByIndex(const Sort&);

	// view subject at input index
	inline void print(int) const;
};

// output sorted subjects by count
struct ViewByCount : public View
{
	inline ViewByCount(const Sort&);

	// view number of subjects specified by input value
	inline void print(int) const;
};

الفئه View يتم تمرير لها فئة الـ sort التى تريد الطباعة بترتبها.

الدالة get_sort تعيد الفئه sort التى تم تمريرها.

الدالة print هى من تقوم بعملية الطباعة و تأخذ معامل رقم صحيح، قيمة هذا المعامل يتم تحديد كيفية استخدامها من عندما يتم الوراثة من هذه الفئة و كتابة كود لهذه الدالة.

 

الفئه View يوجد لها subclasses و هم ViewByIndex و ViewByCount.

 

الدالة print بالفئة ViewByIndex تأخذ منك موقع داخل الترتيب المحدد ليتم طباعته.

الدالة print بالفئة ViewByCount تأخذ منك عدد subject ليتم طباعته بإستخدام الترتيب المحدد. العدد الذى يتم طباعته يبدأ من الموقع صفر (هكذا تم تحديده بالسؤال الأصلي).

 

*****************************************************************************

 

البيانات المدخله للفئه subject لابد من التحقق من محتواها اولا و لابد من اعطاء المستخدم القدره على اعادة المحاولة ان اخطأ، من هذا المبدأ جاءت الفئه Request و الفئات التى تنبثق منها:

// state of request
enum RequestState
{
	Accept,
	Failed,
	Cancel
};

template<typename rtype>
struct Request
{
	inline Request(int);

	inline void reset();

	inline const rtype& answer() const;

	inline int count() const;

	inline int used() const;

	inline RequestState ask();

protected:
	virtual RequestState asking() = 0;

protected:
	rtype value;

private:
	int total; // max number of times to ask
	int asked; // number of times user asked
};

// request subject name
struct ReuqestSubjectName : public Request<std::string>
{
	inline ReuqestSubjectName(int);

	inline RequestState asking();
};

// request subject link
struct ReuqestSubjectLink : public Request<std::string>
{
	inline ReuqestSubjectLink(int);

	inline RequestState asking();
};

// request subject owner
struct ReuqestSubjectOwner : public Request<std::string>
{
	inline ReuqestSubjectOwner(int);

	inline RequestState asking();
};

// request subject date
struct ReuqestSubjectDate : public Request<std::string>
{
	inline ReuqestSubjectDate(int);

	inline RequestState asking();
};

// request subject views
struct ReuqestSubjectViews : public Request<uint32_t>
{
	inline ReuqestSubjectViews(int);

	inline RequestState asking();
};

// request subject replies
struct ReuqestSubjectReplies : public Request<uint32_t>
{
	inline ReuqestSubjectReplies(int);

	inline RequestState asking();
};

الفئه request تقوم بإظهار رسائل للمستخدم للحصول على القيم من المستخدم و من ثم تتحقق من محتواها فإن كان صحيحا يتم قبوله او خاطئ فيتم رفضه او يقوم المستخدم بإلغاء عملية الإدخال.

ايضا الفئه request تسمح بعدد معين من المحاولات.

 

المتغير value يحتفظ بالقيمة التى ادخلها المستخدم و تم قبولها منه.

المتغير total هو اقصي عدد من مرات الإدخال المسموح بها فى حالة الخطأ.

المتغير asked هو عدد المرات التى اخطأ فيها المستخدم فى عملية الإدخال.

 

مشيد الفئه Request يتم إدخال له عدد المرات المسموح للمستخدم بتكرار عملية الإدخال فى حالة الخطأ.

الدالة reset تقوم بإعادة الفئة إلى حالتها الإفتراضية.

الدالة answer تعيد قيمة value.

الدالة count تعيد قيمة total.

الدالة used تعيد قيمة asked.

الدالة ask تقوم بطلب البيانات من المستخدم فى عدد مرات محدد و تعيد قيمة من النوع RequestState و هم:

   - Accept: تعنى ان القيمه المدخله من المستخدم تم قبولها.

   - Failed: تعنى أن المستخدم اخطأ فى عدد مرات تجاوز الحد الاقصي.

   - Cancel: و تعنى ان المستخدم طلب إلغاء عملية الإدخال.

الدالة asking تقوم بعملية الطلب الفعلي من المستخدم فى مرة واحدة فقط و تعيد اى من قيم RequestState. هذه الدالة يتم كتابة الكود الخاص بها فى فئات مشتقه من Request.

 

توجد 7 فئات مشتقه من الفئه Request و هم:

 

ReuqestSubjectName: تقوم بطلب إسم الموضوع.

ReuqestSubjectLink: تقوم بطلب رابط الموضوع.

ReuqestSubjectOwner: تقوم بطلب إسم كاتب الموضوع.

ReuqestSubjectDate: تقوم بطلب تاريخ كتابة الموضوع.

ReuqestSubjectViews: تقوم بطلب عدد مرات مشاهدة الموضوع.

ReuqestSubjectReplies: تقوم بطلب عدد الردود بالموضوع.

 

سبب وجود هذه الفئات هو ان اسلوب التحقق من إسم الموضوع يختلف عن رابطه و ايضا عن تاريخ إنشائه، و لو تم كتابتهم كدوال سيتم تكرار كود التحقق من عدد مرات الإدخال و كذلك حالة الإدخال و هل تم قبول الإجابة المدخله ام لا.

هذه الفئات سيتم إستخدامها من خلال فئة الإدخال Forum.

 

*****************************************************************************

 

الفئه Forum هى من تقوم بإدارة كافة الفئات الباقيه:

struct Forum
{
	// start input operation
	void start();

private:
	// show add-subject menu
	void menu_add();
	// show delete-subject menu
	void menu_del();
	// show update-subject menu
	void menu_update();
	// show view-subject menu
	void menu_view();

private:
	// subject storage
	Store store;
};

الدالة start تقوم ببدء البرنامج و طباعة القائمة الرئيسية و التى يتم من خلال نتيجتها إستدعاء باقي الدوال.

 

الدالة menu_add تقوم بإظهار قائمة ادخال موضوع جديد و تقوم بكل العمليات اللازمه و إذا تمت عملها بنجاح سيتم إدخال موضوع جديد داخل الـ store.

الدالة menu_del تقوم بإظهار قائمة لحذف موضوع موجود و عندما تتم عملها بنجاح سيتم حذف موضوع من الـ store.

الدالة menu_update تقوم بإظهار قائمة لتحديث محتوى موضوع و عندما تتم عملها بنجاح سيتم تحديث موضوع او اكثر داخل الـ store.

الدالة menu_view تقوم بإظهار قائمة لترتيب و عرض المواضيع و عندما تتم عملها بنجاح سيتم عرض المحتوى الذى يطلبه المستخدم بالترتيب الذى يرغب به.

 

العوده للدالة start يتسبب فى طباعة قائمة البدء و يتم الخروج من البرنامج إذا اختار المستخدم ذلك.

 

*****************************************************************************

 

ذكرت من قبل ان الفئه Subject لا تحتوى على دوال الإدخال و السبب يكمن فى استخدام فئات Request لهذا الغرض اثناء الإدخال و التعديل و حيث انهم امرين مختلفين ففصلهم عن فئة subject كان أفضل (من وجهة نظرى).

 

الكود بالمرفقات و يحتوى على ملفات بثلاثة امتدادات و هم:

hpp: ملفات الـ header الخاصة بالمشروع.

inl: ملفات الكود الـ inline و التى تستخدم فى ادارة المشروع ككل.

cpp: و يحتوى على body للدوال التى سنقوم بكتابة الكود لها.

 

الملف main.cpp يحتوى على كود بدء البرنامج.

الملف Makefile يستخدم لإستدعاء مترجم ++g لإنشاء ملف تنفيذى (بغض النظر عن نظام التشغيل)، لتنفيذ محتواه من داخل الـ terminal ادخل على مجلد المشروع و اكتب make و سيتم تنفيذ محتواه.

 

أرجو ان يكون التصميم الحالي أكثر دقة و كفاءه من السابق، و منتظر تعليقك حتى اقوم بوضع النسخه التى سنعمل عليها داخل git.

 

list.zip

 

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة C++er في 22 نوفمبر 2013 في 03:33

1

مدونتي: C++ Tips and Tricks

#13

ما شاء الله .
بدايةً .. التصميم ممتاز .. والعلاقات بين الفئات جيدة جداً .. وجهدك رائع بالفعل :) بارك الله لك .
ووجود الفئة Request مهم بالفعل ..وعملي ..

وهذه أسئلتي :
1- من سيتعامل مع الكائنات من فئات Request ? جربت وضعها داخل Forum.add فكانت غير منطقية
لذلك قمت بإضافة دالة create داخل الفئة Subject .. كما ذكرتَ :

اقتباس
ذكرت من قبل ان الفئه Subject لا تحتوى على دوال الإدخال و السبب يكمن فى استخدام فئات Request لهذا الغرض اثناء الإدخال و التعديل و حيث انهم امرين مختلفين ففصلهم عن فئة subject كان أفضل (من وجهة نظرى).

الفكرة ممتازة , وتتمة لها ..أرى أنه يجب ترك التحكم في Request إلى Subject .. ( آسف عدلت على ملف .inl)
صحح لي الـ implementation  التالي إن لم يكن استخدامي صحيحاً للـrequest
ReuqestSubjectName NameReq(3);
    ReuqestSubjectLink LinkReq(3);
    ReuqestSubjectDate DateReq(3);
    ReuqestSubjectOwner OwnerReq(3);
    ReuqestSubjectReplies RepliesReq(3);
    ReuqestSubjectViews ViewsReq(3);

    if(NameReq.ask()==Accept)
        name=NameReq.answer();
    if(LinkReq.ask()==Accept)
        link=LinkReq.answer();
    if(DateReq.ask()==Accept)
        date=DateReq.answer();
    if(OwnerReq.ask()==Accept)
        owner=OwnerReq.answer();
    if(RepliesReq.ask()==Accept)
        replies=RepliesReq.answer();
    if(ViewsReq.ask()==Accept)
        views=ViewsReq.answer();
إذا كان الكود السابق صحيحاً , فأطلب تغيير فئات الـrequest إلى جملة switch واحدة بداخل دالة asking تتكفل بكل نوع , بدلا من إنشاء كائن لكل واحد .. ( هذا التعديل غير مهم على كل حال)

2- هناك return Failed; ناقصة في نهاية الدالة ask في الملف request.inl

3- هل أنا مصيب بأن كائنات Request هي singleton :) هل سنفعل شيئاً بشأن ذلك ؟

4-ما فهمته من توزيع الملفات هو أنني كـcoder عليّ تعديل ملفات cpp فقط .. وعدم المساس بأي ملف آخر ..
حسناً كما ذكرت .. احتاج دالة Create داخل Subject للتحكم بالـRequests .

5- بحاجة طباعة سطر جديد بعد النجوم التي تظهر بعد كل عرض لعنصر (عندما نعرض أكثر من عنصر )
*-أخيراً سؤال جانبي ..
في C لا يوجد class ولكن يوجد struct إلا أن وظيفته الوحيدة هي جمع عدة أنواع من البيانات فكتلة واحدة ..
المهم .. في ++C وجدت class وصار بالإمكان إضافة توابع خاصة بالفئة .. ولكن أيضاً صار لـstruct الوظيفة نفسها !
مع فارق أن الحالة الافتراضية لمحتويات struct هي public بينما في class هي private .. جميل
طيب لماذا نستخدم struct !?.. هل هناك ما يمنع أن يكون أي structهو class ؟

لا أجد أي عبارة is_sorted=true; داخل دالة الترتيب !!

قمت بكتابة كود أولي , والبرنامج جاهز لتنفيذ العمليات الأساسية , دون ترتيب ..
أرى أن المشروع انتهى دون أن أشارك بشيء :D .

 

اقرأ المرفقات , وأعطني رأيك وملاحظاتك .. ثم أنا جاهز للتعليمات ..

 

وفقك الله

 

Lists.m.rar

#14
اقتباس
1- من سيتعامل مع الكائنات من فئات Request ? جربت وضعها داخل Forum.add فكانت غير منطقية

دوال قائمة الإضافة و التعديل هى من سيقوم بإنشاء request حيث فى الاولي سيكون سببها إضافة subject جديد و فى الثانية بهدف إدخال قيمه جديدة لتعديل مثيلها داخل subject موجود.

 

اقتباس
لذلك قمت بإضافة دالة create داخل الفئة Subject.

عند تصميم برنامج يتم تقسيمه إلى عدة طبقات:

الأولي: و اجهة المستخدم و تمثل عملية الإدخال و الإخراج و التحقق من المدخلات.

الثانية: تحتوى على العمليات التى تمثل منطق البرنامج، فى برامج قواعد البيانات هذه الطبقة تحتوى على العمليات الحسابيه و ربطها معا.

الثالثه: الطبقه الدنيا و تحتوى على وسيلة لحفظ و استرجاع البيانات.

 

هذه الطبقات مع كبر حجم البرنامج سيتم تقسيم كل واحدة منهم إلى اكثر من مرحلة، لاحظ ان هذه الطبقات قد تقسم على برنامج واحد او أكثر.

 

فى هذت المشروع الفئات:

Forum, Request, View هم وسيلة الإدخال و الإخراج فى البرنامج.

Subject و Sort هم منطق البرنامج.

Store هى وسيلة حفظ و استرجاع البيانات.

 

كما ترى وسائل الإدخال و الإخراج يمكن ان يتخاطبوا سويا و لأنهم فى مستوى واحد، ايضا يمكنهم التخاطب مع كل من اسفلهم، و لكن ليس من المنطقي ان تتعامل طبقة دنيا مع طبقة عليا حيث لا يوجد وسيلة للتخاطب.

أعتقد الأن انت ترى لماذا كنت اريد فصل عملية طباعة محتويات subject عنه و تركها داخل view.

 

اقتباس
صحح لي الـ implementation  التالي إن لم يكن استخدامي صحيحاً للـrequest

الكود الذى كتبته قريب و لكن لن يتم استخدام الفئات بهذا الشكل لأنك ستقوم بإنشائهم بالتتابع فقط إن كان الذى يليهم صحيح، بمعنى عند الإدخال ستقوم بطلب الإسم فإن فشلت حينها سيتم إلغاء عملية الإدخال كافة و لن نحتاج إلى انشاء البقيه. أما فى كود التعديل فالمستخدم هو الذى سيحدد ما الذى يريد تعديله، فمثلا عندما تريد تعديل موضوع فأنت لا تعدل كل شئ و لكن قد تعدل عنصر واحد، هذا العنصر هو الذى يحدده المستخدم.

 

اقتباس
إذا كان الكود السابق صحيحاً , فأطلب تغيير فئات الـrequest إلى جملة switch واحدة بداخل دالة asking تتكفل بكل نوع , بدلا من إنشاء كائن لكل واحد .. ( هذا التعديل غير مهم على كل حال)

فى هذه الحالة سيكون لدينا دالة asking و التى من خلالها ستقوم بإدخال كافة المحتويات و بالتالي إذا حدث و ان ادخل المستخدم عدد مشاهدات خاطئة فسيتم إعادة ادخال كافة العناصر مره اخرى حتى يتم الوصول لمره إدخال عدد المشاهدات، او سنقوم بحفظ القيم و تحديد flag معين لنعرف اين وقفنا بحيث عندما ندخل عليها مره اخرى نذهب إلى هذه النقطه.

 

فلنفترض اننا فعلنا اى من هذه الحلول ماذا سيحدث عند التعديل و الذى من المفترض ان يقوم المستخدم بتحديد ما الذى يريد تعديله بالظبط، ايضا عملية تغيير قيمة عنصر بـ subject يتبعها مباشرة عملية احتساب hash لذلك العنصر و هذا الكود موجود داخل دوال set و التى يتم تغيير العناصر منها.

 

و إذ افترضنا اننا تخلصنا من هذه المشاكل تخيل اننا نريد اضافة عنصر جديد و ليكن تقييم الموضوع و هو عدد عشري و ايضا التقييم حينها سنحتاج لتعديل الدالة asking و ايضا ربطها مع قائمة التعديل بـ forum حتى نستطيع معرفة ماذا يريد المستخدم ان يعدل بالتحديد.

 

كل هذه المشاكل تم تلاشيها من خلال وجود فئه request لكل عنصر فإذا اردنا تغيير قيمة عنصر نستخدم الفئة التى تتعامل معه و لا نحتاج لإجراء اى تعديل إلا بإضافة دالة set و get داخل subject و انشاء فئة خاصة بذلك العنصر كي تقوم بالتحقق من محتواه.

 

ايضا تذكر ان التحقق من إسم الموضوع يختلف عن التحقق من رابطه، و حتى وإن لم نقم بتطبيق عمليات التحقق كما يجب ان تكون ففصل هذه العمليات عن بعضها يجعل الكود مقروء وسهل التصحيح فيما بعد.

 

اقتباس
2- هناك return Failed; ناقصة في نهاية الدالة ask في الملف request.inl

نسيت كتابتها.

 

اقتباس
3- هل أنا مصيب بأن كائنات Request هي singleton :) هل سنفعل شيئاً بشأن ذلك ؟

كائنات request ليست singleton لأننا فى التعديل لن نعطى المستخدم اكثر من فرصة عند الخطأ، لأنه يعدل و لا يضيف و هذا الإختلاف يجعل انشاء نسختين مختلفتين من request امر هام و بالتالي هى ليست singleton.

 

اقتباس
4-ما فهمته من توزيع الملفات هو أنني كـcoder عليّ تعديل ملفات cpp فقط .. وعدم المساس بأي ملف آخر ..

حسناً كما ذكرت .. احتاج دالة Create داخل Subject للتحكم بالـRequests .

كما ذكرت لك فئات request هى فى طبقة الإدخال و الإخراج و الفئة subject هى فى طبقة منطق البرنامج و حيث ان طبقة subject أدنى من request فهى لا تراها و بالتالي ليس من المنطقي ربطها معها حيث request قد تنشئ subject و ليس العكس.

 

اقتباس
5- بحاجة طباعة سطر جديد بعد النجوم التي تظهر بعد كل عرض لعنصر (عندما نعرض أكثر من عنصر )

نسيت وضعها هى الأخرى.

 

اقتباس
طيب لماذا نستخدم struct !?.. هل هناك ما يمنع أن يكون أي structهو class ؟

لا يوجد مانع انا استخدم struct لأن الحالة الإقتراضية لعناصره public فقط لا غير.

 

اقتباس
لا أجد أي عبارة is_sorted=true; داخل دالة الترتيب !!

اخر سطر فى الدالة sort بدلا من return true إجعلها return is_sorted = true.

 

اقتباس
قمت بكتابة كود أولي , والبرنامج جاهز لتنفيذ العمليات الأساسية , دون ترتيب ..

أرى أن المشروع انتهى دون أن أشارك بشيء :D .

الملف helper.hpp الموجود بالمرفق تالف.

 

الدالة subject.create منطقيا و فعليا خاطئة، فعليا لأنك لن تدخل رابط الموضوع إذا كان إسم خاطئ، و الإسم الخاطئ اقصد به مساحات فارغة. و منطقيا لأن مكانها داخل subject غير صحيح كما وضحت لك.

 

باقي الأكواد تنقصها بعض التعديلات و الإضافات حتى يصبح البرنامج مكتمل. للعلم انت كتبت اغلب كود البرنامج و التعديلات الباقيه صغيره.

 

إذا كنت موافق على ما قلت أعلمنى حتى ابلغك بالتعديلات التى سنقوم بها.

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#15
اقتباس
Forum, Request, View هم وسيلة الإدخال و الإخراج فى البرنامج.
...
أعتقد الأن انت ترى لماذا كنت اريد فصل عملية طباعة محتويات subject عنه و تركها داخل view.

كنت أرغب بترك Forum نظيفة :)
لكن رأيك هو الصواب .. فهمت عليك
اقتباس
الكود الذى كتبته قريب و لكن لن يتم استخدام الفئات بهذا الشكل لأنك ستقوم بإنشائهم بالتتابع فقط إن كان الذى يليهم صحيح


اقتباس
الكود الذى كتبته قريب و لكن لن يتم استخدام الفئات بهذا الشكل لأنك ستقوم بإنشائهم بالتتابع فقط إن كان الذى يليهم صحيح

خشيت من كتابة الكثير من nested if فشكلها مزعج .. ولكن لا مفرّ :p
اقتباس
و إذ افترضنا اننا تخلصنا من هذه المشاكل تخيل اننا نريد اضافة عنصر جديد و ليكن تقييم الموضوع و هو عدد عشري و ايضا التقييم حينها سنحتاج لتعديل الدالة asking و ايضا ربطها مع قائمة التعديل بـ forum حتى نستطيع معرفة ماذا يريد المستخدم ان يعدل بالتحديد.

نعم فهمت هذه النقطة , فصل الكود أفضل , ولكن ذكّرني أن نختبر قابلية إضافة عنصر جديد لاحقاً (ليس الآن)
اقتباس
و حيث ان طبقة subject أدنى من request فهى لا تراها و بالتالي ليس من المنطقي ربطها معها حيث request قد تنشئ subject و ليس العكس.

إذا هذه هي الفكرة المناسبة ... هل يمكن إضافة دالة بناء Subjectكاملة تتولى قيادة عملية إدخال الموضوع .. ثم تفصّل الإدخال لوحدها إلى إدخالات جزئية
(لا زالت فكرة ترك الـForum نظيفاً تشغلني , أرغب بأن يحوي على التخاطب مع المستخدم بشأن العمليات الأساسية فقط دون أن يغوص في التفاصيل .. ما رأيك ؟ يعني هو فئة قيادة , ولا يجب أن تنزل لمستوى محتويات Subject)
اقتباس
الدالة subject.create منطقيا و فعليا خاطئة، فعليا لأنك لن تدخل رابط الموضوع إذا كان إسم خاطئ، و الإسم الخاطئ اقصد به مساحات فارغة. و منطقيا لأن مكانها داخل subject غير صحيح كما وضحت لك.

نعم فهمت هذه النقطة تماماً .. أظن اننا بحاجة إلى Nested if ونقل الدالة إلى الطبقة العليا ..

حسناً .. أنا موافق بالطبع على كل النقاط .. وبخصوص Create فقد قمت بكتابة دالة "شاردة" هكذا بداخل Forum.cpp وعدلت الأمور التي أفسدتها :p
وللدلالة أيضاً على أنني فهمت قضية التقسيمات العنصرية لطبقات البرنامج , فقد رأيت أن print لا يجب أن تكون داخل Subjectو قمت بكتابة دالة أخرى "شاردة " داخل View.inl مسؤولة عن طباعة عناصر الموضوع الواحد ..

على كل حال ,, قمت بإضافة بعض التعديلات وأضفت Exception جديدة .. وبعض الـ"حشاكيل" الأخرى التي احتجتها اثناء الـimplimentation
أرجو منك الاطلاع عليها ..  وتجربة البرنامج .. ثم اتخاذ القرارات المناسبة دون مناقشتها .. وأنا جاهز
:)
بالتوفيق

أتركك مع شكل CreateSubject .. ثم مع المرفقات
const Subject CreateSubject()
{
    ReuqestSubjectName NameReq(3);//name
    if(NameReq.ask()==Accept){
        ReuqestSubjectLink LinkReq(3);//link
        if(LinkReq.ask()==Accept){
            ReuqestSubjectDate DateReq(3);//date
            if(DateReq.ask()==Accept){
                ReuqestSubjectOwner OwnerReq(3);//owner
                if(OwnerReq.ask()==Accept){
                    ReuqestSubjectReplies RepliesReq(3);//replies
                    if(RepliesReq.ask()==Accept){
                        ReuqestSubjectViews ViewsReq(3);//views
                        if(ViewsReq.ask()==Accept){
                                Subject subject;
                                subject.set_name(NameReq.answer());
                                subject.set_link(LinkReq.answer());
                                subject.set_date(DateReq.answer());
                                subject.set_owner(OwnerReq.answer());
                                subject.set_replies(RepliesReq.answer());
                                subject.set_views(ViewsReq.answer());
                                return subject;
                        }else{/*bad views*/throw BadInputFormatException("views");}
                    }else{/*bad replies*/throw BadInputFormatException("replies");}
                }else{/*bad owner*/throw BadInputFormatException("owner");}
            }else{/*bad date*/throw BadInputFormatException("date");}
        }else{/*bad link*/throw BadInputFormatException("link");}
    }else{/*bad name*/throw BadInputFormatException("name");}
}
في أمان الله :)
 

____________________________

ملاحظة : بسبب التعديلات .. تلخبطت تضمينات المكاتب , سامحني لذلك :p

بالتوفيق

semiFinalListProjectDesign.rar

#16
اقتباس
(لا زالت فكرة ترك الـForum نظيفاً تشغلني , أرغب بأن يحوي على التخاطب مع المستخدم بشأن العمليات الأساسية فقط دون أن يغوص في التفاصيل .. ما رأيك ؟ يعني هو فئة قيادة , ولا يجب أن تنزل لمستوى محتويات Subject)

عندما تفكر بإسلوب معين لفترة طويلة يصبح من الصعب التخلي عنها.

 

إليك مثال سيجعلك تقتنع بالفكرة التى ارغب إيصالها لك.

 

البرنامج يعمل على console و عمليات الإدخال و الإخراج كلها تتم بإستخدام standard input/output و هذا هو وظيفة الفئات Forum/Request/View.

سنفترض ان الدالة print الموجوده بـ subject موجوده داخل view.

 

السؤال الأن ماذا سيحدث اذا اردنا كتابة هذا البرنامج بإستخدام visual tools، ما الأجزاء التى سنحتاج لإعادة كتابتها؟

الإجابة: الأجزاء التى سنحتاج لإعادة كتابتهم هم الفئات Forum/Request/View او بمعنى اشمل الفئات التى تتعامل مع المستخدم، الان تخيل وجود كود يربط بين فئات الطبقات الثلاث و الكابوس الذى سنعيش فيه لفصل هذه الأكواد عن بعضها لكتابة برنامج يستخدم visual tools.

 

مثال اخر فى تغيير مخزن البيانات من مجرد class إلى قاعدة بيانات، حينها كل ما ستحتاج لتغييره هو الكود الخاص بالفئه Store فبدلا من ان تتعامل مع vector ستقوم بالإتصال بقاعدة البيانات و الحصول على البيانات منها ..... إلخ.

 

وجود protocol محدد بين اجزاء البرنامج يجعل استبدال احدهم بأخر امر يسير.

 

اقتباس
نعم فهمت هذه النقطة تماماً .. أظن اننا بحاجة إلى Nested if ونقل الدالة إلى الطبقة العليا ..

هل استخدمت من قبل مصفوفة من الـ function pointer ، إليك الكود الذى تكره بشكل افضل:

bool get_name(Subject& s)
{
    ReuqestSubjectName req(3);
    if( req.ask() != Accept ) return false;
    s.set_name( req.answer() );
    return true;
}

bool get_link(Subject& s)
{
    ReuqestSubjectLink req(3);
    if( req.ask() != Accept ) return false;
    s.set_link( req.answer() );
    return true;
}

bool get_date(Subject& s)
{
    ReuqestSubjectDate req(3);
    if( req.ask() != Accept ) return false;
    s.set_date( req.answer() );
    return true;
}

bool get_owner(Subject& s)
{
    ReuqestSubjectOwner req(3);
    if( req.ask() != Accept ) return false;
    s.set_owner( req.answer() );
    return true;
}

bool get_replies(Subject& s)
{
    ReuqestSubjectReplies req(3);
    if( req.ask() != Accept ) return false;
    s.set_replies( req.answer() );
    return true;
}

bool get_views(Subject& s)
{
    ReuqestSubjectViews req(3);
    if( req.ask() != Accept ) return false;
    s.set_views( req.answer() );
    return true;
}

الدوال السابقه سيتم إستخدامها بالشكل التالي:

typedef bool(Forum::*get_fn)(Subject&);Subject subject;get_fn[] call[] ={    &Forum::get_name, &Forum::get_link, &Forum::get_date,    &Forum::get_owner, &Forum::get_replies, &Forum::get_views};for(int i=0; i< 6; ++i){    if (!(this->*call)(subject))    {        cout << "invalid inputs: please check your inputs and try again.\n";        break;    }    store.add(subject);}

مع العلم ان هذه الطريقة هى افضل لإضافة حالات جديدة و فصل كود التحقق من بعضهم، وايضا يمكن استخدام preprocessor لتوليد الكود بدلا من كتابته يدويا او حتى استخدام الـ template، إلا انه يعيبها اخذ عنوان الدوال و الذى سيجعل المترجم فى الأغلب بإنشائهم كدوال.

 

إذا كانت هذه الطريق ممناسبة لك، يمكننا تطبيقها و الإستغناء عن nested if (لا حظ اننا يمكننا إستبدال nested if بعدة جمل if مع flag).

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#17
اقتباس

و عمليات الإدخال و الإخراج كلها تتم بإستخدام standard input/output و هذا هو وظيفة الفئات Forum/Request/View.

كان آخر اقتراح هو تسليم الـ Request مسألة دخل وخرج الـSubject بدلاً من Forum ...

اقتباس

إليك مثال سيجعلك تقتنع بالفكرة التى ارغب إيصالها لك.

مقتنع تماماً .. لا مجال لنقاش أهمية فصل الطبقات الثلاثة .. أنا معك .. :)

 

آلية عمل مصفوفة التوابع  أكثر فعالية .. ( لاحظ أنك لم تستخدم المؤشرات أبداً في التصميم غير في هذه النقطة ..)

أنا جاهز للبدء ..

(بالنسبة لـ git يمكنك رفع المشروع عليه , ولكن يبدو أنني سأرفع عملي على المنتدى فلم أتمكن من استخدامه لمشاكل تقنية :P )

 

بالتوفيق

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 23 نوفمبر 2013 في 22:47

#18
اقتباس
كان آخر اقتراح هو تسليم الـ Request مسألة دخل وخرج الـSubject بدلاً من Forum ...

لا لم أقل هذا بل قلت ان:

 

1- عملية ادخال محتويات الـ subject تتم من request حيث تقوم بطلب البيانات من المستخدم ثم تقوم بالتحقق منها و من ثم حفظها داخل value إن كانت صحيحة.

2- View ستأخذ Sort و تقوم بالطباعة تبعا له.

3- Forum مسئولة عن اظهار القوائم الرئيسية و تمرير القيم إلى الدوال الخاصه بالعملية التى يريدها المستخدم.

 

اقتباس
لاحظ أنك لم تستخدم المؤشرات أبداً في التصميم غير في هذه النقطة

أردت استخدام المرجع حيث مع تفعيل التحسينات تعطى نتيجة افضل، لاحظ ايضا ان طريقة المؤشرات هى احد الإختيارات المتاحة.

 

اقتباس
(بالنسبة لـ git يمكنك رفع المشروع عليه , ولكن يبدو أنني سأرفع عملي على المنتدى فلم أتمكن من استخدامه لمشاكل تقنية :P )

لا مشكلة سنقوم بكتابة الكود و سنرفع النتيجة مره واحدة فقط، حيث كل منا سيرفع الجزء الذى انتهى منه ثم سأقوم بدمجهم معا و التحقق من النتائج لكافة الحالات ثم ارفع النسخة النهائية هنا.

 

قبل ان احدد الجزء الذى سيعمل كل منا فيه، يرجى حذف السطر التالي من الدوال التى ستقوم بالعمل عليها.

// TODO: add logic here

سأقوم بكتابة كود الفئة Forum و فئاتة Sort و قم انت بكتابة كود الفئة Subject و فئات Request.

قمت بكتابة كود forum.start و subject.hash_string. الكود بالمرفقات.

 

بالنسبة لعملية hash التاريخ يمكنك تطبيق الطريقة التى ذكرتها من قبل او يمكنك اختيار الطريقة التى تريد مع محاولة تقليل تشابه النتائج (إلا لو كانت المدخلات واحدة بالطبع).

 

حتى تستطيع اختبار الكود الخاص بك قم بكتابة كود اختبار داخل دوال القوائم و بعد ان تنتهي قم بحذفه قبل ارفاقه.

سأقوم بوضع الكود الخاص بى بعد ان انتهي منه.

 

list.zip

 

 

و الله ولي التوفيق

تم تعديل هذه المشاركة بواسطة C++er في 24 نوفمبر 2013 في 22:57

مدونتي: C++ Tips and Tricks

#19

//(قصدت آخر اقتراح لي )

 

حسناً سأبدأ بإذن الله .. وسأعيد قراءة الموضوع من البداية حتى أركز على جميع الملاحظات ..

وسأعدّل هذه المشاركة خلال الأيام القادمة لتحوي العمل المنجز بإذن الله

 

جزاك الله خيراً

________________________

تم رفع الملف .. باسم 1.1.0.27.11.2013 التاريخ واضح بيمين السلسلة .. أما الرقم 1 على اليسار فهو رقمي كـcoder (أنت رقمك صفر :D ) .. والباقي 1.0 الـ version

أياً يكن ..

لم أعدل سوى request.cpp و subject.cpp وبانتظار باقي الأوامر سيّدي :]

بالتوفيق

1.1.0.27.11.2013.rar

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 27 نوفمبر 2013 في 23:21

#20

انا اوشكت على الإنتهاء و لدي سؤال، هل ادخال كافة محتويات الـ subject إجبارى أم هناك اجبارى و اخر غير إجبارى؟

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#21

نعم ستكون جميع الحقول إجبارية ..

لم أتوقّع أن يكون الكود بهذا الحجم .. كلما كتبت سطر أفكّر في أنه يحتوي المزيد من المحدّدات .. وأظن أننا بحاجة إلى عدة جولات ذهاب وإياب بين الكود والتجربة حتى نصل إلى مستوى جيد في التطبيق

 

وفقك الله

#22

قمت بالإنتهاء من نسختى و هى بالملف list.

list.zip

 

التعديلات التى قمت بها:

1- داخل makefile قمت بإضافة الأجزاء subject, request, forum حتى تستطيع ترجمة ملف واحد فقط، إسم الملف الذى سيتم ترجمته هو بنفس الإسم المدخل، للإستخدام:

make         // compile all and create executable
make subject // compile subject.cpp and create object file
make request // compile request.cpp and create object file
make forum   // compile   forum.cpp and create object file

2- قمت بكتابة الجزء الخاص بي من الكود الخاص بالملف forum.cpp

3- قمت بحذف الملف sort.cpp و قمت بإضافة كود الترتيب داخل الملف sort.inl

4- قمت بإضافة []operator داخل الفئة subject و يتم تمرير لها رقم و بناءا عليه يتم العوده بـ hash لأحد عناصر subject

5- قمت بإضافة الدالة get_store داخل الفئة forum

6- قمت بتصحيح تعريف inline الموجود بالفئه subject داخل الملفين بالإمتداد hpp و inl

7- قمت بتصحيح تعريف inline الموجود بالفئه request داخل الملفين بالإمتداد hpp و cpp

 

قمت بدمج الملفات التى قمت بتعديلها مع الملفات التى قمت أنت بتعديلها و دمجتهم فى ملف واحد بالإسم list_all.

list_all.zip

 

لاحظ اننى لم استطع تجربة الكود الخاص بي حتى بعد دمج الملفات بسبب أخطأ منطقية فى الكود الخاص بك، قم بمراجعته و سأقوم انا بكتابة دالة تقوم بإستقبال ملف يحتوى على مجموعة بيانات مسبقة للإختبار فقط لتسهيل عملية إختبار البيانات.

 

 

و الله ولي التوفيق

1

مدونتي: C++ Tips and Tricks

#23

بارك الله فيك .. نعم في كودي أخطاء كثيرة لأنني لم أختبره .. على كل حال النسخة جاهزة الآن ويمكنك اختبارها
وتجد في الملف المضغوط أيضاً ملف باسم TestCases يمكنك نسخ محتواه إلى الـconsole فور بدء البرنامج لإضافة بعض العناصر
وهذا هو الملف

هناك بعض الأمور أثناء تجربة البرنامج لا بد أنك ستلاحظها بنفسك , ولكن لا بأس من استباق ذكل..

بالنسبة لـview
1-عند الدخول في view لا يوجد مخرج بعد العرض

if (choice == 2)
        {
            for(;;)
            {
                cout << "Choose:\n"
                        "\t[1] Subject by index\n"
                        "\t[2] Number of subjects\n"
                        "\t[3] All subjects\n";
2- عند اختيار العرض بجميع العناصر يتم العرض بشكل طبيعي أما عند اختيار العرض باستخدام رقم العنصر يتم رمي استثناء عدم الترتيب
أقصد أن الكود هنا
if (get2 == 3)
                {
                    sort->sort();
                    ViewByCount(*sort).print(count);
                    continue;
                }
وبالتحديد هذا السطر
                    sort->sort();
ربما يكون جيداً وجوده في باقي طرق العرض أيضاً

بالنسبة لـsort
أرى أنك كتبت كود quick sort بنفسك :) ألا تثق بالدالة qsort ...
في الكود
if (!is_sorted)        throw SubjectsNotSortedException();
لماذا لا نعتبر الحالة السابقة هي الترتيب الافتراضي  بدلا من إنهاء البرنامج

c = get_value<char>() | 0x020;
حركة رائعة جداً !!

يا رجل:)
حتى هذه لم تفتك
if (count > 1) cout << "[1] Sort   ";
وهذه :)
((right - left) >> 1)
:D

عند الترتيب يتم تنفيذ الكود
cout << "invalid choice, please try again.\n";
دوماً

أرجو منك تجريب الكود وتنبيهي إلى أي أخطاء تراها .. وعند إتمام ذلك يكون حل السؤال قد اكتمل
شكراً لأنك سايرتني في الموضوع :D استفدت منك كثيراً جزاك الله خيراً
_______________________
لدي سؤال أخير  ...
كيف يجب أن تكون المرحلة التالية في التطوير ؟
فالأفكار التطويرية كثيرة .. وتشمل أحياناً مجالاً كبيراً من الكود , مثلاً
1- القراءة والكتابة من وإلى الملفات (دوال جديدة )
2- إنشاء قاعدة بيانات خاصة بالفئة request فعندي بعض الأفكار حول التحقق من المدخلات لمنع بعض الكلمات مثلاً في العناوين أو الأسماء (تتطلب تغييرات في الفئة request فضلاً عن إنشاء القوائم وإمكانية التعديل عليها)
3- إنشاء واجهة مستخدم رسومية.. ويمكننا القيام بذلك دون المساس بأي جزء داخل الأكواد .. (لدي فكرة يمكن أن تحل هذا الأمر)
4- تمديد فئة الـ Subject بشكل عام .. (أي شيء يمكن إضافته) مثل جعل عدد الردود قائمة من الردود أو إضافة تقييم للمواضيع وما إلى ذلك .. سيتطلب إضافة دوال كثيرة لـ getو set وللتحكم بشكل عام

كيف يجب أن تتم إضافة الدوال ؟ فأحياناً يتطلّب ذلك إضافة دوال أخرى في فئات خارج نطاق العضو
وإلى أي حد يجب تطوير الكود فليس من المعقول معالجة جميع الأخطاء دوماً أو تنفيذ جميع التطويرات مرة واحدة
وأيضاً من الصعب اعتبار كود ما , منتهٍ ولن يتم المساس به بعد الآن ..
وشكراً لك :)
وفقك الله وسدد خطاك

1.1.1.1.12.2013.rar

#24
اقتباس
1-عند الدخول في view لا يوجد مخرج بعد العرض

نسيت اضافة شرط الخروج.

 

اقتباس
2- عند اختيار العرض بجميع العناصر يتم العرض بشكل طبيعي أما عند اختيار العرض باستخدام رقم العنصر يتم رمي استثناء عدم الترتيب

ههه، و انا كنت اعتقد ان الكود الوحيد الذى كتبته و الذى سيكون خالي من الأخطاء هو الخاص بالترتيب، ربنا يستر على البقيه.

 

 

اقتباس
ربما يكون جيداً وجوده في باقي طرق العرض أيضاً

لم أفهم قصدك.

 

 

اقتباس
أرى أنك كتبت كود quick sort بنفسك :) ألا تثق بالدالة qsort ...

لم تكن لتنفع بأى حال من الاحوال.

 

 

اقتباس
لماذا لا نعتبر الحالة السابقة هي الترتيب الافتراضي  بدلا من إنهاء البرنامج

هذا السطر موجود داخل الدالة at بالفئة sort و السبب في رمي الإستثناء هو جعل الترتيب له اولوية قبل الحصول على القيم التي تريدها، و لو اننا جعلنا الترتيب الإفتراضي هو الأساس إذا لم يتم الترتيب فهذا سيتسبب فى رجوع قيم منطقيا غير صحيحة بالنسبة لنوع الترتيب و طبيعته.

 

 

اقتباس
عند الترتيب يتم تنفيذ الكود

هذا خطأ اخر

 

 

اقتباس
لدي سؤال أخير  ...

سأقوم بالإجابة عليه بعد ان ارى الكود و اختبره.

 

 

و الله ولي التوفيق

مدونتي: C++ Tips and Tricks

#25
اقتباس

اقتباس
ربما يكون جيداً وجوده في باقي طرق العرض أيضاً

لم أفهم قصدك.

أعني أن الكود

                if (get2 == 3)
                {
                    sort->sort();
                    ViewByCount(*sort).print(count);
                    continue;
                }

                if (get2 == 2)
                {
                    cout << "Enter number of subjects [1";
                    ...
                        ViewByCount(*sort).print(get2);
                    continue;
                }

                if (get2 == 1)
                {
                    cout << "Enter subject index [0";
                    ...
                        ViewByIndex(*sort).print(get2);
                    continue;
                }
يُفترض أن يكون
                if (get2 == 3)
                {
                    sort->sort();
                    ViewByCount(*sort).print(count);
                    continue;
                }

                if (get2 == 2)
                {
                /****/sort->sort();
                    cout << "Enter number of subjects [1";
                    ...
                        ViewByCount(*sort).print(get2);
                    continue;
                }

                if (get2 == 1)
                {
                /****/sort->sort();
                    cout << "Enter subject index [0";
                    ...
                        ViewByIndex(*sort).print(get2);
                    continue;
                }

بالتوفيق :)

1

مواضيع مشابهة