أقرب تمثيل لروبي لمتغير فئة "نهائي ثابت خاص" و"نهائي ثابت عام" في Java؟

StackOverflow https://stackoverflow.com/questions/2441524

سؤال

بالنظر إلى كود Java أدناه، ما هو أقرب ما يمكن أن تمثل هذين الاثنين static final المتغيرات في فئة روبي؟وهل من الممكن في روبي التمييز بينهما private static و public static المتغيرات كما هو الحال في جافا؟

public class DeviceController
{
  ...
  private static final Device myPrivateDevice = Device.getDevice("mydevice");
  public static final Device myPublicDevice = Device.getDevice("mydevice");
  ...
  public static void main(String args[])
  {
   ...
  }
}
هل كانت مفيدة؟

المحلول

لا يوجد حقًا بناء مكافئ في روبي.

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

لا أستطيع حقًا التأكد من المشكلة التي تحاول حلها من خلال مقتطف التعليمات البرمجية الصغير هذا، ولكن هنا واحد فكرة محتملة لكيفية تنفيذها في روبي:

class DeviceController
  class << self
    def my_public_device;  @my_public_device  ||= Device['mydevice'] end

    private

    def my_private_device; @my_private_device ||= Device['mydevice'] end
  end
end

هنا آخر:

class DeviceController
  @my_public_device  ||= Device['mydevice']
  @my_private_device ||= Device['mydevice']

  class << self
    attr_reader :my_public_device, :my_private_device
    private :my_private_device
  end
end

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

دعونا نتناول بعض المفاهيم هنا.

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

[ملحوظة:عندما أكتب شيئًا مثل "لا مفر"، "دائمًا"، "الطريقة الوحيدة" وما إلى ذلك، فهي في الواقع لا تعني "لا سبيل إلا للتفكير".في هذه الحالة بالذات، هناك Object#instance_variable_set, ، على سبيل المثال.]

بعبارة أخرى:في روبي، تكون المتغيرات دائمًا خاصة، والطريقة الوحيدة للوصول إليها هي عبر طريقة getter و/أو setter، أو، كما يطلق عليها في Ruby، قارئ و/أو كاتب السمات.

والآن أواصل الكتابة عنه متغيرات سريعة, ، ولكن في مثال Java لدينا الحقول الثابتة, ، أي. فصل المتغيرات.حسنًا، في روبي، على عكس Java، تعتبر الفئات كائنات أيضًا.وهي أمثلة على Class class وهكذا، تمامًا مثل أي كائن آخر، يمكن أن يكون لديهم متغيرات الحالة.لذلك، في روبي، ما يعادل متغير الفئة هو في الواقع مجرد متغير مثيل قياسي ينتمي إلى كائن يصادف أنه فئة.

(توجد أيضًا متغيرات التسلسل الهرمي للفئة، يُشار إليها بعلامة مزدوجة @@sigil.هذه أشياء غريبة حقًا، وربما ينبغي عليك تجاهلها.تتم مشاركة متغيرات التسلسل الهرمي للفئة عبر التسلسل الهرمي للفئة بأكملها، أي.الفئة التي ينتمون إليها، وجميع فئاتها الفرعية وفئاتها الفرعية وفئاتها الفرعية ...وأيضا كافة مثيلات كل تلك الفئات.في الواقع، فهي تشبه المتغيرات العالمية أكثر من متغيرات الفئة.ينبغي حقا أن يتم استدعاؤهم $$var بدلاً من @@var, ، نظرًا لأنها ترتبط ارتباطًا وثيقًا بالمتغيرات العامة مقارنة بمتغيرات الحالة.إنها ليست عديمة الفائدة تمامًا ولكنها نادرًا ما تكون مفيدة.)

لذلك، قمنا بتغطية الجزء "الحقل" (حقل Java == متغير مثيل روبي)، وقمنا بتغطية الأجزاء "العامة" و"الخاصة" (في روبي، تكون متغيرات المثيل دائمًا خاصة، إذا كنت تريد جعلها عامة، استخدم طريقة getter/setter العامة) وقمنا بتغطية الجزء "الثابت" (حقل Java الثابت == متغير مثيل فئة Ruby).ماذا عن الجزء "الأخير"؟

في Java، "النهائي" هو مجرد طريقة مضحكة لتهجئة "const"، والتي تجنبها المصممون لأن const الكلمات الرئيسية في لغات مثل C وC++ معطلة بمهارة ولم يرغبوا في إرباك الناس.روبي يفعل تحتوي على ثوابت (يُشار إليها بالبدء بحرف كبير).لسوء الحظ، فهي ليست ثابتة حقًا، لأن محاولة تعديلها، أثناء إصدار تحذير، تنجح بالفعل.لذا، فهي أقرب إلى اتفاقية منها إلى قاعدة يفرضها المترجم.ومع ذلك، فإن التقييد الأكثر أهمية للثوابت هو أنها عامة دائمًا.

لذا فإن الثوابت تكاد تكون مثالية:لا يمكن تعديلها (حسنًا، هم لا ينبغي يتم تعديلها)، أي.هم final, ، أنهم ينتمون إلى فئة (أو وحدة)، أي.هم static.لكنهم دائما كذلك public, ، لذا للأسف لا يمكن استخدامها كنموذج private static final مجالات.

وهذه هي بالضبط النقطة التي يأتي فيها التفكير في المشكلات بدلاً من الحلول.ما هو الذي تريد؟تريد الدولة ذلك

  1. ينتمي إلى فئة،
  2. يمكن قراءتها فقط وليس كتابتها
  3. تتم تهيئته مرة واحدة فقط و
  4. يمكن أن تكون خاصة أو عامة.

يمكنك تحقيق كل ذلك، ولكن بطريقة مختلفة تمامًا عن Java:

  1. متغير مثيل الفئة
  2. لا توفر طريقة الضبط، بل فقط طريقة getter
  3. استخدم روبي ||= مهمة مركبة لتعيين مرة واحدة فقط
  4. طريقة جيتر

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

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


تماما مختلف نهج الخصوصية هو النهج المستخدم في اللغات الوظيفية:استخدام الإغلاق.عمليات الإغلاق هي كتل من التعليمات البرمجية التي تغلق على بيئتها المعجمية، حتى بعد أن تكون تلك البيئة المعجمية خارج النطاق.تحظى هذه الطريقة في تنفيذ الدولة الخاصة بشعبية كبيرة في Scheme، ولكن تم نشرها مؤخرًا بواسطة Douglas Crockford et al.لجافا سكريبت.إليك مثال في روبي:

class DeviceController
  class << self
    my_public_device, my_private_device = Device['mydevice'], Device['mydevice']

    define_method :my_public_device  do my_public_device  end
    define_method :my_private_device do my_private_device end

    private :my_private_device
  end # <- here the variables fall out of scope and can never be accessed again
end

لاحظ الفرق الدقيق ولكن المهم بين الإصدارات الموجودة أعلى إجابتي:عدم وجود @ سيجيل.هنا، نحن نخلق محلي المتغيرات، لا مثال المتغيرات.بمجرد انتهاء نص الفصل، تخرج هذه المتغيرات المحلية من النطاق ولا يمكن الوصول إليها مرة أخرى أبدًا. فقط لا يزال بإمكان الكتلتين اللتين تحددان طريقتي getter الوصول إليهما، لأنهما تغلقان على نص الفصل.الآن، هم حقًا خاص و هم final, ، لأن الشيء الوحيد في البرنامج بأكمله الذي لا يزال بإمكانه الوصول إليهم هو الملف النقي getter طريقة.

ربما لا تكون هذه روبي اصطلاحية، ولكن بالنسبة لأي شخص لديه خلفية Lisp أو JavaScript، يجب أن يكون واضحًا بدرجة كافية.كما أنها أنيقة جدًا.

نصائح أخرى

أقرب شيء يمكنني التفكير فيه في متغير نهائي هو وضع المتغير في السؤال كمتغير مثيل من الوحدة النمطية:

class Device
    # Some static method to obtain the device
    def self.get_device(dev_name)
        # Need to return something that is always the same for the same argument
        dev_name
    end
end

module FinalDevice
    def get_device
        # Store the device as an instance variable of this module...
        # The instance variable is not directly available to a class that
        # includes this module.
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice
    def initialize
        # Creating an instance variable here to demonstrate that an
        # instance of Foo cannot see the instance variable in FinalDevice,
        # but it can still see its own instance variables (of course).
        @my_instance_var = 1
    end
end

p Foo.new

p (Foo.new.get_device == Foo.new.get_device)

هذا المخرجات:

#<Foo:0xb78a74f8 @my_instance_var=1>
true

الخدعة هنا هي أنه من خلال تثبيت الجهاز في وحدة نمطية، يمكنك فقط الوصول إلى الجهاز من خلال هذه الوحدة. من الفصل Foo, ، لا توجد طريقة لتعديل أيّ الجهاز الذي تحصل عليه، دون التمثيل مباشرة على Device الطبقة أو FinalDevice وحدة. ال freeze اتصل FinalDevice قد يكون أو لا يكون مناسبا، اعتمادا على احتياجاتك.

إذا كنت ترغب في إجراء ملحق عام وخاص، يمكنك تعديل Foo مثله:

class Foo
    include FinalDevice

    def initialize
        @my_instance_var = 1
    end

    def get_device_public
        get_device
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

في هذه الحالة ربما تحتاج إلى تعديل FinalDevice::get_device لاتخاذ حجة كذلك.

تحديث: أشار @ لبنان ذلك @fin كما أعلن في FinalDevice في الواقع يمكن الوصول إليها بمثيل Foo. وبعد لقد افترضني بضغط أنه لأنه لم يكن في إخراج النص الافتراضي بواسطة Foo#inspect, لم يكن في الداخل Foo.

يمكنك علاج هذا من خلال جعل أكثر صراحة @fin مثال على متغير FinalDevice وحدة:

class Device
    def self.get_device(dev_name)
        dev_name
    end
end

module FinalDevice
    def get_device
        FinalDevice::_get_device
    end

    protected
    def self._get_device
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice

    def get_device_public
        get_device
    end

    def change_fin
        @fin = 6
        @@fin = 8
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

f = Foo.new
x = f.get_device_public
f.change_fin
puts("fin was #{x}, now it is #{f.get_device_public}")

المخرجات بشكل صحيح:

fin was my_device, now it is my_device
class DeviceController
  MY_DEVICE = Device.get_device("mydevice")
end

و نعم، require 'device' إذا لزم الأمر.

على الرغم من أن لا شيء سوف يمنعك من إعادة تعريف ثابت في مكان آخر، إلا أن تحذير :)

ثابت ثابت في روبي:

class DeviceController
    @@my_device = Device.get_device("mydevice")
end

ثابت العام في روبي:

class DeviceController       
    def self.my_device; @@my_device; end

    @@my_device = Device.get_device("mydevice")
end

لا يمكن ل Ruby عدم وجود "نهائي" :)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top