لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 10/27/20 في كل الموقع
-
الفكرة في padding و margin أن هم لهم استخدامات معينة واستخدامهم في غير مكانهم ينشئ مشكلة في الصفحة وفي الريسبونسيف . أولاً padding هو هامش داخل العنصر إذا كنت تريد إنشاء هامش داخل العنصر مثل border كبير الحجم من اليسار او اليمين او من جميع الأتجهات وتكون العناصر منسقة بداخله ولكن لا تستخدم padding في جعل العنصر في الوسط في width او في ال height لكن تستخدم في إضافة هامش بسيط وإذا كان الهامش أكبر إلى حد ما يفضل استخدام النسبة المئوية . div{ padding: 25px; } واستخدام margin بنفس الفكرة ولكنه هامش خارج العنصر بمعني أن width الخاص بالعنصر سوف يظل كما هو عند تعين margin معين من أي جانب عكس padding وهو يستخدم في التفريق بين عنصرين وجعل هامش بينهم فوق أو يمين في أي اتجاه . ولكن تقسم div ليس من خصائصهم يقسم بواسطة width وال height . أنصحك بمشاهدة خاصية flexbox جيداً من هنا 6 فيديوهات سوف تتعلم كيفية تقسم div بطريقة صحيحة وهي الطريقة التي يستخدمها المحترفين الأن .2 نقاط
-
سنستعرض في هذا الدرس بعض أنماط التصميم الشهيرة في C++ ثم سنتطرق سريعًا إلى مفهوم إعادة التصميم (Refactoring) والنمط Goto Cleanup المتَّبع. نمط المحوِّل (Adapter Pattern) يتيح نمط المحوِّل للأصناف غير المتوافقة أن تعمل معًا، والسبب الأساسي في استخدامه تكمن في أنّه يمكّن المطوّرين من إعادة استخدام البرامج الموجودة عبر تعديل الواجهة فقط. يعتمد نمط المحول على تركيب الكائنات (object composition). العميل يستدعي العملية على المحوِّل. يستدعي المحوِّل الكائن المحوَّل Adaptee لتنفيذ العملية. تُبنى المكدّسات (stacks) في مكتبة القوالب القياسية STL على المتجهات، فمثلًا: عندما ينفّذ المُكدّس الدّالةَ push()، فإنّ المتجه الأساسي (underlying vector) سينفذ التابع vector::push_back(). انظر المثال التالي: #include <iostream> // الواجهة المقصودة class Rectangle { public: virtual void draw() = 0; }; // المركّب القديم - المحوَّل class LegacyRectangle { public: LegacyRectangle(int x1, int y1, int x2, int y2) { x1_ = x1; y1_ = y1; x2_ = x2; y2_ = y2; std::cout << "LegacyRectangle(x1,y1,x2,y2)\n"; } void oldDraw() { std::cout << "LegacyRectangle: oldDraw(). \n"; } private: int x1_; int y1_; int x2_; int y2_; }; // Adapter wrapper مغلِّف المحوَّل class RectangleAdapter: public Rectangle, private LegacyRectangle { public: RectangleAdapter(int x, int y, int w, int h): LegacyRectangle(x, y, x + w, y + h) { std::cout << "RectangleAdapter(x,y,x+w,x+h)\n"; } void draw() { std::cout << "RectangleAdapter: draw().\n"; oldDraw(); } }; int main() { int x = 20, y = 50, w = 300, h = 200; Rectangle * r = new RectangleAdapter(x, y, w, h); r -> draw(); } // :الخرج //LegacyRectangle(x1,y1,x2,y2) //RectangleAdapter(x,y,x+w,x+h) شرح الشيفرة أعلاه: يعتقد العميل أنّه يتحدث إلى Rectangle الهدف هو الصنف Rectangle، وهو الذي سيستدعي العميلُ التابعَ عليه. Rectangle * r = new RectangleAdapter(x, y, w, h); r -> draw(); لاحظ أنّ صنف المحوِّل (adapter class) يستخدم الوراثة المتعدّدة. class RectangleAdapter: public Rectangle, private LegacyRectangle { ... } يتيح المحوِّلRectangleAdapter للصنف LegacyRectangle الاستجابة للطلب (استدعاء draw() على Rectangle) عن طريق وراثة الصنفين معًا. لا يملك الصنف LegacyRectangle نفس التوابع (draw()) التي يملكها Rectangle، لكن يمكن أن يأخذ Adapter(RectangleAdapter) استدعاءات التابعِ Rectangle ثمّ يعود لاستدعاء التابع LegacyRectangle على oldDraw(). class RectangleAdapter: public Rectangle, private LegacyRectangle { public: RectangleAdapter(int x, int y, int w, int h): LegacyRectangle(x, y, x + w, y + h) { std::cout << "RectangleAdapter(x,y,x+w,x+h)\n"; } void draw() { std::cout << "RectangleAdapter: draw().\n"; oldDraw(); } }; يُترجمُ نمط المحوِّل واجهةَ صنف معيّن إلى واجهة أخرى متوافقة، ولكن مختلفة. لذلك، فهو يشبه نمط الوكيل من حيث أنّه مغلّف أحادي المكوّنات (single-component wrapper)، لكن قد تكون واجهة الصنف المحوَّل والصنف الأصلي مختلفة. ويمكن استخدام نمط المحوِّل لإظهار واجهة برمجية (API) معيّنة للسماح لها بالعمل مع شيفرات أخرى كما رأينا في المثال أعلاه. أيضًا، يمكننا أن نأخذ واجهات غير متجانسة، ونحوّلها لواجهة برمجية موحّدة ومتسقة. لدى نمط الجسر بنية مشابهة للكائنات المحوِّلة، بيْد أنّ للجسور هدفًا مختلفًا، إذ يُرادُ منها فصل الواجهة عن التقديم، حتّى يسهل تعديلها بشكل مستقل. أمّا المحوِّل فيُراد منه تعديل واجهة كائن موجود. نمط المراقب (Observer pattern) الهدف من نمط المراقب هو تعريف اعتمادية واحد-إلى-متعدد (one-to-many) بين الكائنات، بحيث إذا تغيرت حالة كائن تُرسل إشعارات إلى جميع الكائنات المتعلّقة به وتحديثها تلقائيًا. يعرِّف الهدف (subject) والمراقبُون (observers) اعتمادية الواحد-إلى-متعدد، وفي هذه الاعتمادية يعتمد المراقبون على الأهداف، وعندما تتغيّر حالة الهدف يتم إشعار المراقبين تلقائيًا. وبناءً على ذلك يمكن تحديث المراقبين وإعطاؤهم قيمًا جديدة. فيما يلي مثال من كتاب "Design Patterns" من تأليف جاما (Gamma). #include <iostream> #include <vector> class Subject; class Observer { public: virtual ~Observer() = default; virtual void Update(Subject&) = 0; }; class Subject { public: virtual ~Subject() = default; void Attach(Observer& o) { observers.push_back(&o); } void Detach(Observer& o) { observers.erase(std::remove(observers.begin(), observers.end(), &o)); } void Notify() { for (auto* o : observers) { o->Update(*this); } } private: std::vector<Observer*> observers; }; class ClockTimer: public Subject { public: void SetTime(int hour, int minute, int second) { this -> hour = hour; this -> minute = minute; this -> second = second; Notify(); } int GetHour() const { return hour; } int GetMinute() const { return minute; } int GetSecond() const { return second; } private: int hour; int minute; int second; }; class DigitalClock: public Observer { public: explicit DigitalClock(ClockTimer& s) : subject(s) { subject.Attach(*this); } ~DigitalClock() { subject.Detach( *this); } void Update(Subject& theChangedSubject) override { if ( &theChangedSubject == &subject) { Draw(); } } void Draw() { int hour = subject.GetHour(); int minute = subject.GetMinute(); int second = subject.GetSecond(); std::cout << "Digital time is " << hour << ":" << minute << ":" << second << std::endl; } private: ClockTimer& subject; }; class AnalogClock: public Observer { public: explicit AnalogClock(ClockTimer& s): subject(s) { subject.Attach( *this); } ~AnalogClock() { subject.Detach( * this);} void Update(Subject& theChangedSubject) override { if ( &theChangedSubject == &subject) { Draw(); } } void Draw() { int hour = subject.GetHour(); int minute = subject.GetMinute(); int second = subject.GetSecond(); std::cout << "Analog time is " << hour << ":" << minute << ":" << second << std::endl; } private: ClockTimer& subject; }; int main() { ClockTimer timer; DigitalClock digitalClock(timer); AnalogClock analogClock(timer); timer.SetTime(14, 41, 36); } الخرج: Digital time is 14: 41: 36 Analog time is 14: 41: 36 توضّح النقاط التالية ملخّص نمط المراقب: تستخدم الكائنات (DigitalClock أو AnalogClock) واجهات الموضوع (Attach() أو Detach()) إمّا لأجل الاشتراك (subscribe) كمراقبين، أو إلغاء الاشتراك (إزالة أنفسهم) من كونهم مراقبين (subject.Attach(*this);، subject.Detach(*this);. يمكن أن يكون لكل موضوع عدّة مراقبين (vector<Observer*> observers;). يحتاج جميع المراقبين إلى تنفيذ واجهة المراقب (Observer interface). لدى هذه الواجهة تابع واحد فقط، وهو Update()، ويُستدعى عند تغيّر حالة الموضوع (Update(Subject &)) بالإضافة إلى التابعين Attach() و Detach()، ينفِّذ الهدف الحقيقي التابعَ Notify() الذي يُستخدم لتحديث جميع المراقبين الحاليين عندما تتغيّر الحالة، لكن تتم جميعها في هذه الحالة في الصنف الأب، Subject (Subject::Attach (Observer&) و void Subject::Detach(Observer&) و void Subject::Notify(). قد يحتوي الكائن الحقيقي أيضًا على توابع لضبط قيمة حالته، أو الحصول عليها. يمكن أن تكون المراقبات الحقيقية أيّ صنف ينفذ واجهة المراقب (Observer interface)، ويشترك كل مراقب مع هدف حقيقي ليحصل على التحديثات (subject.Attach(*this);). كائنا نمط المراقب مترابطان بشكل طفيف، إذ يمكنهما التفاعل مع بعضهما البعض، لكنّ معرفتها ببعضهما محدودة. الإشارات والفتحات (Slots) الإشارات والفتحات (Slots) هي بنية لغوية قُدِّمت في Qt، وتسهّل على المطوّرين تقديم نمط المراقب دون الحاجة لاستخدام الشيفرات المُتداولة (boilerplate code). الفكرة الرئيسية وراء الإشارات والفتحات هي أنّ عناصر التحكم (controls) التي تُعرف أيضًا باسم الودجات widgets) يمكنها إرسال إشارات تحتوي على معلومات حول الحدث، والتي يمكن استقبالها من قبل عناصر تحكم أخرى باستخدام دوال خاصة تُعرف باسم الفتحات (slots)، وهي أعضاء أصناف في Qt. يتوافق نظام الإشارة / الفتحة مع تصميم واجهات المستخدم الرسومية، كما يمكن استخدام نظام الإشارة-الفتحة للدخل / الخرج غير المتزامن (asynchronous I/O) بما في ذلك المقابس sockets، والأنابيب pipes، والأجهزة التسلسلية serial devices، وغيرها مما يختص بإشعارات الأحداث أو لربط أزمنة الأحداث (timeout events) مع نُسخ الكائن والتوابع أو الدوالّ المناسبة. لا يلزم كتابة شيفرة خاصة بالتسجيل/إلغاء التسجيل/الاستدعاء، لأنّ الكائن الوصفي للمصرّف (Meta Object Compiler أو اختصارًا MOC) الخاصّ بـ Qt يولّد البنية الأساسية اللازمة تلقائيًا . تدعم لغة C# أيضًا إنشاءات مشابهة، لكنها تستخدم مصطلحات وصيغة مختلفة: فالإشارات تسمّى أحداثًا، والفتحات تسمّى مفوِّضَات (delegates). إضافة إلى ذلك يمكن أن يكون المفوّض متغيّرًا محليًا، مثل مؤشّرات الدوال، بينما يجب أن تكون الفتحة في Qt عضوًا في صنف. نمط المصنع (Factory Pattern) يقسّم نمط المصنع (Factory pattern) عمليّة إنشاء الكائنات، ويتيح الإنشاء بالاسم باستخدام واجهة مشتركة: class Animal { public: virtual std::shared_ptr < Animal > clone() const = 0; virtual std::string getname() const = 0; }; class Bear: public Animal { public: virtual std::shared_ptr < Animal > clone() const override { return std::make_shared < Bear > ( * this); } virtual std::string getname() const override { return "bear"; } }; class Cat: public Animal { public: virtual std::shared_ptr < Animal > clone() const override { return std::make_shared < Cat > ( *this); } virtual std::string getname() const override { return "cat"; } }; class AnimalFactory { public: static std::shared_ptr < Animal > getAnimal(const std::string& name) { if (name == "bear") return std::make_shared < Bear > (); if (name == "cat") return std::shared_ptr < Cat > (); return nullptr; } }; نمط الباني يفصل نمط الباني (Builder Pattern) عملية إنشاء الكائن عن الكائن نفسه، والفكرة الرئيسية وراء ذلك هي أنّ الكائن ليس عليه مسؤولية إنشائه، وقد تكون عمليّة تصريف الكائنات المعقّدة مهمّة معقدة في حدّ ذاتها، لذا يمكن تفويض هذه المهمة إلى صنف آخر. سأنشئ فيما يلي بانيًا بريديًا Email Builder بلغة C++، وهو مستوحى من فكرة مشابهة في في C#، كائن البريد الإلكتروني ليس بالضرورة كائنًا معقدًا، ولكنّه مثال جيد لتوضيح كيفية عمل هذا النمط. #include <iostream> #include <sstream> #include <string> using namespace std; // التصريح اللاحق للباني class EmailBuilder; class Email { public: friend class EmailBuilder; // Email يمكن للباني الوصول إلى الأعضاء الخاصة في static EmailBuilder make(); string to_string() const { stringstream stream; stream << "from: " << m_from << "\nto: " << m_to << "\nsubject: " << m_subject << "\nbody: " << m_body; return stream.str(); } private: Email() = default; // قصر الإنشاء على الباني string m_from; string m_to; string m_subject; string m_body; }; class EmailBuilder { public: EmailBuilder& from(const string &from) { m_email.m_from = from; return *this; } EmailBuilder& to(const string &to) { m_email.m_to = to; return *this; } EmailBuilder& subject(const string &subject) { m_email.m_subject = subject; return *this; } EmailBuilder& body(const string &body) { m_email.m_body = body; return *this; } operator Email&& () { return std::move(m_email); // لاحظ عمليّة النقل } private: Email m_email; }; EmailBuilder Email::make() { return EmailBuilder(); } // مثال إضافي std::ostream& operator << (std::ostream& stream, const Email& email) { stream << email.to_string(); return stream; } int main() { Email mail = Email::make().from("me@mail.com") .to("you@mail.com") .subject("C++ builders") .body("I like this API, don't you?"); cout << mail << endl; } بالنسبة للإصدارات الأقدم من C++، يمكن تجاهل عملية std::move وإزالة && من عامل التحويل لكنّ هذا سيؤدّي إلى إنشاء نسخة مؤقتة. ينهي المنشئ عمله عندما يُرسَل البريد الإلكتروني بواسطة operator Email&&(). يكون المنشئ في هذا المثال كائنًا مؤقتًا، ويعيد البريدَ الإلكتروني قبل تدميره. يمكنك أيضًا استخدام عملية صريحة مثل Email EmailBuilder::build() {...} بدلًا من عامل التحويل. تمرير الباني من الميزات الرائعة التي يوفّرها "نمط الباني" هي القدرةُ على استخدام عدّة عوامل (actors) لإنشاء كائن معيّن، ويمكن ذلك عن طريق تمرير المنشئ إلى العوامل الأخرى، والتي سيعطي كل منها بعض المعلومات الإضافية للكائن المبنِيّ. هذا مفيد بشكل خاص عندما تريد بناء الاستعلامات query، أو إضافة المُرشِّحات، وغيرها من المواصفات. void add_addresses(EmailBuilder& builder) { builder.from("me@mail.com") .to("you@mail.com"); } void compose_mail(EmailBuilder& builder) { builder.subject("I know the subject") .body("And the body. Someone else knows the addresses."); } int main() { EmailBuilder builder; add_addresses(builder); compose_mail(builder); Email mail = builder; cout << mail << endl; } الكائنات القابلة للتغيير يمكنك تغيير تصميم نمط الباني بما يناسب احتياجاتك، سنوضّح هذا الأمر في هذه الفقرة: كائن البريد الإلكتروني في المثال السابق كان غير قابل للتغيير (immutable)، أي أنّه لا يمكن تعديل خاصّياته لأنّه لا يمكن الوصول إليها، وقد كانت هذه الميزة مطلوبة، لكن ماذا لو كنت بحاجة إلى تعديل الكائن بعد إنشائه، سيكون عليك أن توفّر له بعض الضوابط (setters). ولمّا كانت تلك الضوابط تتكرّر في المنشئ، فقد تفكر في جمعها جميعًا في صنف واحد -لن تكون هناك حاجة للصنف الباني إذن-. لكن قد يكون الأفضل جعل الكائن المبنِيّ قابلاً للتغيير. نمط تصميم المفردة (Singleton Design Pattern) التهيئة المُرجأة (Lazy Initialization) عثرت على هذا المثال في قسم Q & A في هذا الرابط. انظر أيضًا هذه المقالة للحصول على تصميم بسيط لتقييم مُرجأ مع مفردة مضمونة التدمير. انظر المثال التالي عن مفردة تقليدية ذات تقييم مُرجأ ومُدمَّرة بشكل صحيح. class S { public: static S& getInstance() { static S instance; // تدميرها مضمون // تُستنسخ عند أوّل استخدام return instance; } private: S() {}; // القوسان المعقوصان ضروريان هنا // C++ 03 // ======== // لا تنس التصريح عن هذين الاثنين، احرص على أن يكونا غير مقبولين // وإلّا فقد تُنسخ المفردة S(S const&); // لا تنفذها void operator=(S const&); // لا تنفذها // C++ 11 // ======= // بإمكاننا أيضا استخدام طريقة حذف التوابع، لكنّنا لن نفعل public: S(S const& ) = delete; void operator = (S const& ) = delete; }; ملاحظة: ذكر سكوت مايرز (Scott Meyers) في كتابه Effective Modern C++ أن التوابع المحذوفة يجب أن تكون عامة، فذلك يسهل اكتشاف الأخطاء لأن رسائل الخطأ تكون أفضل حينها، فالمصرِّفات تتحقق من قابلية الوصول (accessibility) قبل الحالة المحذوفة. يمكنك معرفة المزيد عن المتفرّدات من الروابط التالية: توضّح هذه الصفحة متى يجب استخدام نمط المفردة: نمط المفردة Singleton في موسوعة حسوب راجع هاتين المقالتين الأجنبيتين لمزيد من المعلومات حول ترتيب التهيئة وكيفية التعامل معها: Static variables initialisation order Finding C++ static initialization order problems تصف هذه المقالة الأجنبية دورة الحياة لمتغير ساكن في دالة ++C: What is the lifetime of a static variable in a C++ function? تناقش المقالة الأجنبية التالية بعض تأثيرات الخيوط على المفردات: Singleton instance declared as static variable of GetInstance method توضّح هذه المقالة الأجنبية لماذا لن يعمل قفل التحقق المزدوج (double checked locking) في C++: What are all the common undefined behaviours that a C++ programmer should know about? المفردات الساكنة الآمنة من إلغاء التهيئة (Static deinitialization-safe singleton) قد تعتمد بعض الكائنات الساكنة (static objects) في بعض الحالات على المفردة، وقد ترغب في ضمان منع تدميرها إلا عند عدم الحاجة إليها. لأجل ذلك يمكن استخدام std::shared_ptr لمنع تدمير المُتفرّدات وإبقائها متاحة لجميع من يستخدمها حتى عندما تُستدعى المدمّرات الساكنة في نهاية البرنامج: class Singleton { public: Singleton(Singleton const&) = delete; Singleton& operator=(Singleton const&) = delete; static std::shared_ptr < Singleton > instance() { static std::shared_ptr < Singleton > s { new Singleton }; return s; } private: Singleton() {} }; ملاحظة: يظهر هذا المثال كإجابة في قسم الأسئلة والأجوبة في موقع SO. المفردات الآمنة خيطيًا (Thread-safe Singeton) الإصدار ≥ C++ 11 يضمن معيار C++ 11 أنّ كائنات نطاق الدوالّ (function scope objects) تُهيَّأ بطريقة متزامنة، ويمكن استخدام هذا لتقديم مفردة آمنة خيطيًا مع تهيئة مُرجأة. class Foo { public: static Foo& instance() { static Foo inst; return inst; } private: Foo() {} Foo(const Foo&) = delete; Foo& operator =(const Foo&) = delete; }; الأصناف الفرعية (Subclasses) انظر المثال التالي: class API { public: static API& instance(); virtual~API() {} virtual const char* func1() = 0; virtual void func2() = 0; protected: API() {} API(const API& ) = delete; API& operator = (const API& ) = delete; }; class WindowsAPI: public API { public: virtual const char* func1() override { /* شيفرة ويندوز */ } virtual void func2() override { /* شيفرة ويندوز */ } }; class LinuxAPI: public API { public: virtual const char* func1() override { /* شيفرة لينكس */ } virtual void func2() override { /* شيفرة لينكس */ } }; API& API::instance() { #if PLATFORM == WIN32 static WindowsAPI instance; #elif PLATFORM = LINUX static LinuxAPI instance; #endif return instance; } المُصرّف في هذا المثال يربط الصنف API بالصنف الفرعي المناسب، من أجل الوصول إلى API حتّى لو لم يكن مربوطًا بشيفرة مخصوصة بمنصّة معينة. تقنيات إعادة التصميم (Refactoring Techniques) يشير مفهوم إعادة البناء (Refactoring) إلى تعديل الشيفرة واستبدال نسخة مُحسّنة بها، ورغم أنّ إعادة البناء تُجرى غالبًا أثناء تغيير الشيفرة بُغية إضافة بعض الميزات أو تصحيح الأخطاء، إلّا أنّ هذا المصطلح مخصوص أساسًا بعمليات تحسين الشيفرة بدون إضافة ميزات أو تصحيح الأخطاء. Goto Cleanup يُستخدم أحيانًا نمط التصميم goto cleanup في شيفرات C++ التي بُنِيت على شيفرات مكتوبة بلغة C، ونظرًا لأنّ الأمر goto يصعِّب فهم سير عمل الدوال، فغالبًا ما يُوصى بتجنّبه. ويمكن استبدال تعليمة return أو الحلقات أو الدوال بالأمر goto. بالمقابل، يتيح استخدام goto cleanup التخلُّص من منطق التنظيف (cleanup logic). short calculate(VectorStr **data) { short result = FALSE; VectorStr *vec = NULL; if (!data) goto cleanup; //< return false يمكن أن يُستعاض عنها بـ result = TRUE; cleanup: delete[] vec; return result; } في C++، يمكنك استخدام تقنية RAII لحلّ هذه المشكلة: struct VectorRAII final { VectorStr *data { nullptr }; VectorRAII() = default; ~VectorRAII() { delete[] data; } VectorRAII(const VectorRAII & ) = delete; }; short calculate(VectorStr **data) { VectorRAII vec {}; if (!data) return FALSE; //< return false يمكن الاستعاضة عنها بـ return TRUE; } بعد هذا، يمكنك الاستمرار في إعادة بناء الشيفرة. مثلًا، عن طريق استبدال VectorRAII بمؤشّر فريد std::unique_ptr أو متّجه std::vector. هذا الدرس جزء من سلسلة دروس عن C++. ترجمة -بتصرّف- للفصل Chapter 112: Design pattern implementation in C++ والفصل Chapter 113: Singleton Design Pattern من كتاب C++ Notes for Professionals1 نقطة
-
1 نقطة
-
عليك في البداية استخدام Firebase Cloud Messaging. من ثم يمكنك استدعاء ال API بواسطة PHP من خلال الكود في الأسفل فقط عليك تغير key وtoken <?php define('API_ACCESS_KEY','Api key from Fcm add here'); $fcmUrl = 'https://fcm.googleapis.com/fcm/send'; $token='235zgagasd634sdgds46436'; $notification = [ 'title' =>'title', 'body' => 'body of message.', 'icon' =>'myIcon', 'sound' => 'mySound' ]; $extraNotificationData = ["message" => $notification,"moredata" =>'dd']; $fcmNotification = [ //'registration_ids' => $tokenList, //multple token array 'to' => $token, //single token 'notification' => $notification, 'data' => $extraNotificationData ]; $headers = [ 'Authorization: key=' . API_ACCESS_KEY, 'Content-Type: application/json' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$fcmUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fcmNotification)); $result = curl_exec($ch); curl_close($ch); echo $result;1 نقطة
-
1 نقطة
-
اهلا بك عزيزي اذا كان لا توجد خدمة service كيف يتم عمل نظام الاشعارات اذن للمستخدم في فلاتر؟1 نقطة
-
نعم، المقصود هو التعويض بقيمة x في f و رسم x مع (f(x. حيث أن السطر 24 يقوم بتكوين دالة كثيرة حدود من القيم المعطاة للدالة poly1d p = np.poly1d([1, 2, 3]) print(p) # الناتج 2 1 x + 2 x + 3 يمكنك قراءة المزيد من numpy.poly1d و numpy.polyfit و python-matplotlib-plot-function1 نقطة
-
لا توجد طريقة للقيام بذلك مباشرة من flutter في الوقت الحالي على الرغم من أن ذلك قد يتغير في مرحلة ما - يمكنك مراجعة iOS/Android Background Execution. لديك خياران رغم ذلك. الخيار الأول هو استخدام MethodChannels وكتابة رمز android الذي تريده لإنشاء خدمة في الخلفية. والخيار الثاني هو مزيج من هذين الملحقين (plugins) - android_alarm_manager و android_intent. لكن هذا لن يساعد في جميع حالات الاستخدام.1 نقطة
-
بالإضافة إلى ما ذكره الزملاء. يوجد chrome extensions إضافات للمتصفح يقوم باستخداها المطوريين و المصممين لقياس الأبعاد و المسافات في الصفحة. يمكنك أن تجد بعضًا منها هنا. و لكن عادةً نحن لا نقوم بالقياس بهذه الطريقة.1 نقطة
-
العلاقات بين أجزاء الكيان الواحد من حيث نسبة رياضية، والتناسب بهذا الشكل يمكن اعتباره قيمة عددية معبرة عن كيفية تواجد عناصر التصميم داخل الإطار العام له. أما بخصوص الصورة التي شاركتها في سؤالك هي متتالية فيبوناتشي والتي تتكون من الأرقام التالية : 0 – 1 – 1 – 2 – 3 – 5 – 8 – 13 – 21 – 34 – 55 – 89 – 144 - ....... وهكذا، وتعتمد المتتالية على اختيار أول رقمين ثم نكمل باقى أعداد المتتالية عن طريق جمع الرقمين السابقين له، فلو جمعنا 0 + 1 = 1 وإذا جمعنا 1+1 = 2 وهكذا مع باقى الارقام.1 نقطة
-
اسمح لي اكلمك بمصطلحات عامة وليست احترافية لتقريب وجهة النظر - UX ليس تصميم كما تتخيله بل هو خريطة لتشغيل التطبيق وتصور لجميع الخطوات والاحتياجات التي يحتاجها مستخدم البرنامج لتوفيرها بطريقة بسيطة - UI هو دة التصميم وهو شكل ولون التطبيق وعدد صفحاته وفونط الكتابة (شكل فقط بدون وظائف) - بعدها المطور بياخد التصميم وبيناقش جدواه من ناحية البرمجة وبيتم تعديل UI بما يتلائم مع المطور وبما يحافظ على UX ثم يبدأ في برمجته ليجعل الازرار مفعلة - المطور بياخد التصميم من المصمم UI UX بالطريقة المناسبة له * بالنسبة لي كمصمم UX UI بقوم بعمله ببرنامج adobeXD الذي يسهل على المطور استلام العمل لانه بيكون مفكك ولا يحتاج منه لتقطيع مثال لتصميم ال UI تم بناءا على رغبة العميل من ألوان ورؤية https://www.behance.net/gallery/78177335/UI-011 نقطة
-
من أهم العوامل في نجاح تصميم الشعار هو أتباع قواعد النسبة الذهبية في تصميمه، لأن من خلال هذه النسبة تجعل أي شكل متوازنا وسارا للعين البشرية، وكما تعلم فإن النسبة الذهبية سارية في الطبيعة بشكل كبير، وهي مهمة من أجل أساسيات الوزن البصري في التصميم الجرافيكي. معظم الشعارات العالمية المشهورة تتبع للنسبة الذهبية من آبل وناشيونال جيوغرافيك وبيبسي وBP وغيرها الكثير من الشعارات المهمة في العالم. يمكنك القراءة أكثر عن الوزن البصري وتحقيقه من خلال الرابط هنا.1 نقطة
-
الخطأ أنك تستخدم (==) لفحص التساوي و هذا الرمز نستخدمه لفحص إذا كان المرجع هو نفسه، لفحص تساوي سلسلتين نصيتين من حيث القيمة نستخدم الدالة equals و هذه الدالة حساسة للحروف و بالتالي فإن الكود يُصبح بالشكل التالي: if(message.equals(words.get(0))){ System.out.println(reply+"Hi!, how are you "+name); message = sca.next(); } إذا كنت تريد فحص تساوي القيمتين مع عدم أخذ طبيعة الحروف بعين الإعتبار يُمكنك إستخدام equalsIgnoreCase. بإمكانك تجربة المثال من خلال هذا: الرابط بالتوفيق.1 نقطة
-
1 نقطة
-
1 نقطة