জাভা এর এনাম দিয়ে একটি সিঙ্গলটন বাস্তবায়নের ডাউনসাইডগুলি কী কী?


14

.তিহ্যগতভাবে, একটি সিঙ্গলটন সাধারণত হিসাবে প্রয়োগ করা হয়

public class Foo1
{
    private static final Foo1 INSTANCE = new Foo1();

    public static Foo1 getInstance(){ return INSTANCE; }

    private Foo1(){}

    public void doo(){ ... }
}

জাভা এনাম দিয়ে, আমরা হিসাবে একটি একক বাস্তবায়ন করতে পারেন

public enum Foo2
{
    INSTANCE;

    public void doo(){ ... }
}

২ য় সংস্করণটি ততটাই দুর্দান্ত, এর কোনও ডাউনসাইড রয়েছে কি?

(আমি এটি কিছু চিন্তাভাবনা দিয়েছি এবং আমি আমার নিজের প্রশ্নের উত্তর দেব; আশা করি আপনার আরও ভাল উত্তর আছে)


16
অবক্ষয়টি হ'ল এটি একটি সিঙ্গলটন। একটি সম্পূর্ণ ওভাররেড ( কাশি ) "প্যাটার্ন"
থমাস এডিং

উত্তর:


32

এনাম সিঙ্গলেটনের কিছু সমস্যা:

একটি বাস্তবায়ন কৌশল প্রতিশ্রুতিবদ্ধ

সাধারণত, "সিঙ্গেলটন" একটি প্রয়োগের কৌশল বোঝায়, কোনও এপিআই স্পেসিফিকেশন নয়। Foo1.getInstance()সর্বজনীনভাবে ঘোষণা করা খুব বিরল যে এটি সর্বদা একই উদাহরণটি ফিরে আসবে। যদি প্রয়োজন হয় তবে প্রয়োগটি Foo1.getInstance()বিবর্তিত হতে পারে, উদাহরণস্বরূপ, প্রতিটি থ্রেডে একটি উদাহরণ ফেরত দেওয়া।

সঙ্গে Foo2.INSTANCEআমরা সর্বজনীনভাবে ঘোষণা করছি যে এই ক্ষেত্রটিকেই হয় উদাহরণস্বরূপ, এবং যে পরিবর্তনের কোনো সুযোগ। একক উদাহরণ থাকার বাস্তবায়ন কৌশল উন্মুক্ত এবং প্রতিশ্রুতিবদ্ধ।

এই সমস্যাটি পঙ্গু নয়। উদাহরণস্বরূপ, কার্যকরভাবে প্রতি-থ্রেড দৃষ্টান্তটি Foo2.INSTANCE.doo()রাখতে থ্রেড স্থানীয় সহায়ক সহায়িকার উপর নির্ভর করতে পারে ।

এনাম ক্লাস বাড়ানো হচ্ছে

Foo2একটি সুপার ক্লাস প্রসারিত Enum<Foo2>। আমরা সাধারণত সুপার ক্লাস এড়াতে চাই; বিশেষত এক্ষেত্রে, সুপার ক্লাসটি জোর করা Foo2হওয়ার সাথে কিছুই করার নেই Foo2। এটি আমাদের অ্যাপ্লিকেশনটির ধরণের শ্রেণিবিন্যাসের দূষণ। আমরা যদি সত্যিই একটি সুপার ক্লাস চাই, সাধারণত এটি একটি অ্যাপ্লিকেশন শ্রেণি, তবে আমরা পারি না, Foo2এর সুপার ক্লাসটি স্থির।

Foo2কিছু মজাদার উদাহরণ পদ্ধতি উত্তরাধিকার সূত্রে প্রাপ্ত name(), cardinal(), compareTo(Foo2), যা কেবলমাত্র Foo2ব্যবহারকারীদের জন্য বিভ্রান্তিকর । এমনকি যদি এর পদ্ধতিটি ইন্টারফেসে পছন্দসই হয় তবে Foo2তার নিজস্ব name()পদ্ধতি থাকতে পারে না Foo2

Foo2 এছাড়াও কিছু মজার স্থির পদ্ধতি রয়েছে

    public static Foo2[] values() { ... }
    public static Foo2 valueOf(String name) { ... }
    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)

যা ব্যবহারকারীদের কাছে অযৌক্তিক বলে মনে হচ্ছে। একটি সিঙ্গলটনের সাধারণত পালবিক স্ট্যাটিক পদ্ধতি থাকা উচিত নয় (বাদে getInstance())

Serializability

সিলেটলেটদের স্টেটফুল হওয়া খুব সাধারণ বিষয়। এই সিলেটলেটগুলি সাধারণত সিরিয়ালযোগ্য হওয়া উচিত নয় । আমি কোনও বাস্তববাদী উদাহরণের কথা ভাবতে পারি না যেখানে রাষ্ট্রীয় সিঙ্গলটনকে একটি ভিএম থেকে অন্য ভিএম-তে পরিবহন করা বোধগম্য হয়; একটি সিঙ্গেলনের অর্থ "এক ভিএম এর মধ্যে অনন্য", "মহাবিশ্বের অনন্য" নয়।

যদি সিরিয়ালাইজেশন সত্যই রাষ্ট্রীয় সিঙ্গলটনের জন্য অর্থবোধ করে, তবে সিঙ্গলটনের স্পষ্টভাবে এবং সুনির্দিষ্টভাবে উল্লেখ করতে হবে যে অন্য ভিএম-তে যেখানে একই ধরণের সিঙ্গলটন ইতিমধ্যে বিদ্যমান থাকতে পারে সেখানে সিঙ্গেলটনকে ডিসরিয়ালাইজ করার অর্থ কী does

Foo2স্বয়ংক্রিয়ভাবে একটি সরলিক সিরিয়ালাইজেশন / ডিসরিয়ালাইজেশন কৌশল প্রতিশ্রুতিবদ্ধ। এটি ঘটে যাওয়ার অপেক্ষায় কেবল দুর্ঘটনা। আমাদের কাছে যদি ডেটা ট্রি থাকে ধারণামূলকভাবে Foo2ভিএম 1- এর স্থিতিগত ভেরিয়েবলকে টি 1 এ উল্লেখ করা হয়, সিরিয়ালাইজেশন / ডেসারিয়ালাইজেশনের মাধ্যমে মানটি আলাদা মান হয়ে যায় - Foo2টিএম-তে ভিএম 2 এর একই ভেরিয়েবলের মান, বাগ সনাক্ত করার জন্য একটি হার্ড তৈরি করে। এই বাগটি Foo1নিঃশব্দে অনির্বাচনযোগ্যতে ঘটবে না ।

কোডিংয়ের সীমাবদ্ধতা

এমন কিছু জিনিস রয়েছে যা সাধারণ ক্লাসে করা যায় তবে enumক্লাসে নিষিদ্ধ । উদাহরণস্বরূপ, কনস্ট্রাক্টরে স্থির ক্ষেত্র অ্যাক্সেস করা। প্রোগ্রামারটি যেহেতু তিনি একটি বিশেষ শ্রেণিতে কাজ করছেন সেহেতু আরও যত্নবান হতে হবে।

উপসংহার

এনগমে পিগিগ্যাকিংয়ের মাধ্যমে আমরা 2 টি লাইনের কোড সংরক্ষণ করি; তবে দামটি খুব বেশি, এনামগুলির সমস্ত ব্যাগেজ এবং বিধিনিষেধ আমাদের বহন করতে হবে, আমরা অজান্তেই এনামের "বৈশিষ্ট্যগুলি" পেয়েছি যার অনিচ্ছাকৃত পরিণতি রয়েছে। একমাত্র কথিত সুবিধা - স্বয়ংক্রিয় সিরিয়ালাইজিবিলিটি - এটি একটি অসুবিধে পরিণত হয়েছে।


2
-১: আপনার সিরিয়ালাইজেশন নিয়ে আলোচনা ভুল। প্রক্রিয়া সরল নয়, কারণ এনস্যামাইজেশনের সময় এনামগুলিকে নিয়মিত দৃষ্টান্তগুলির থেকে খুব আলাদাভাবে বিবেচনা করা হয়। সমস্যা বর্ণনা করে না ঘটতে প্রকৃত deserialization প্রক্রিয়া একটি "রাষ্ট্র পরিবর্তনশীল" এর সংশোধন করে না।
স্কার্ফ্রিজ

বিভ্রান্তির এই উদাহরণটি দেখুন: কোডরঞ্চ.আট
498782

2
আসলে লিঙ্কযুক্ত আলোচনাটি আমার বক্তব্যকে জোর দেয়। আপনার উপস্থিতি দাবি করা সমস্যা হিসাবে আমি কী বুঝেছিলাম তা আমাকে ব্যাখ্যা করতে দিন। কিছু বস্তু এ একটি দ্বিতীয় বস্তু বি উল্লেখ করে The এখন আমরা এনাম-বেসড সিঙ্গলটনের একটি পূর্বে সিরিয়ালযুক্ত উদাহরণটি (যা সিরিয়ালাইজেশনের সময়, বি 'রেফারেন্স করা হয়েছে! আসলে যা ঘটে তা হ'ল, এটি A এবং S রেফারেন্স বি, কারণ বি 'সিরিয়ালযুক্ত হবে না। আমি ভেবেছিলাম আপনি প্রকাশ করতে চেয়েছিলেন যে A এবং S একই জিনিসটিকে আর উল্লেখ করে না।
স্কার্ফ্রিজ

1
আমরা কি সত্যিই একই সমস্যার কথা বলছি না?
স্কার্ফ্রিজে

1
@ কেভিন ক্রামুইয়েড: Constructor<?> c=EnumType.class.getDeclaredConstructors()[0]; c.setAccessible(true); EnumType f=(EnumType)MethodHandles.lookup().unreflectConstructor(c).invokeExact("BAR", 1);উদাহরণস্বরূপ একটি দুর্দান্ত উদাহরণ হ'ল:; Constructor<?> c=Thread.State.class.getDeclaredConstructors()[0]; c.setAccessible(true); Thread.State f=(Thread.State)MethodHandles.lookup().unreflectConstructor(c).invokeExact("RUNNING_BACKWARD", -1);^), জাভা 7 এবং জাভা 8… এর অধীনে পরীক্ষিত…
হোলার

6

একটি এনাম উদাহরণ ক্লাস লোডার উপর নির্ভরশীল। অর্থাত্ যদি আপনার দ্বিতীয় শ্রেণীর লোডার থাকে তবে একই এনাম ক্লাসটি পিতা বা মাতা হিসাবে প্রথম শ্রেণীর লোডার না থাকে তবে আপনি মেমরিতে একাধিক উদাহরণ পেতে পারেন।


কোড নমুনা

নিম্নলিখিত এনাম তৈরি করুন এবং তার। ক্লাস ফাইলটি নিজেই একটি জারে রাখুন। (অবশ্যই জারের সঠিক প্যাকেজ / ফোল্ডার কাঠামো থাকবে)

package mad;
public enum Side {
  RIGHT, LEFT;
}

উপরের এনামের কোনও অনুলিপি শ্রেণীর পথে নেই তা নিশ্চিত করে এখন এই পরীক্ষাটি চালান:

@Test
public void testEnums() throws Exception
{
    final ClassLoader root = MadTest.class.getClassLoader();

    final File jar = new File("path to jar"); // Edit path
    assertTrue(jar.exists());
    assertTrue(jar.isFile());

    final URL[] urls = new URL[] { jar.toURI().toURL() };
    final ClassLoader cl1 = new URLClassLoader(urls, root);
    final ClassLoader cl2 = new URLClassLoader(urls, root);

    final Class<?> sideClass1 = cl1.loadClass("mad.Side");
    final Class<?> sideClass2 = cl2.loadClass("mad.Side");

    assertNotSame(sideClass1, sideClass2);

    assertTrue(sideClass1.isEnum());
    assertTrue(sideClass2.isEnum());
    final Field f1 = sideClass1.getField("RIGHT");
    final Field f2 = sideClass2.getField("RIGHT");
    assertTrue(f1.isEnumConstant());
    assertTrue(f2.isEnumConstant());

    final Object right1 = f1.get(null);
    final Object right2 = f2.get(null);
    assertNotSame(right1, right2);
}

এবং আমাদের কাছে দুটি একই বস্তু রয়েছে যা "একই" এনাম মান উপস্থাপন করে।

আমি একমত যে এটি একটি বিরল এবং স্বীকৃত কোণার কেস এবং প্রায় সবসময় জাভা সিঙ্গলটনের জন্য একটি এনাম ব্যবহার করা যেতে পারে। আমি নিজেই করি তবে সম্ভাব্য ডাউনসাইড সম্পর্কে জিজ্ঞাসা করা প্রশ্ন এবং সতর্কতার এই নোটটি সম্পর্কে জানার পক্ষে মূল্যবান।


আপনি কি উদ্বেগের কোনও রেফারেন্স পেতে পারেন?

আমি এখন উদাহরণ কোড সহ আমার প্রাথমিক উত্তরটি সম্পাদনা ও বর্ধিত করেছি। আশা করি তারা পয়েন্টটি চিত্রিত করতে এবং মাইকেলটির প্রশ্নের উত্তরও উত্তর দিতে সহায়তা করবে।
ম্যাড জি

@ মিশেলটি: আমি আশা করি যে আপনার প্রশ্নের উত্তর :-)
ম্যাড জি

সুতরাং, যদি সিঙ্গেলটনের জন্য এনাম (শ্রেণির পরিবর্তে) ব্যবহারের কারণটি কেবল এটির সুরক্ষা ছিল, তবে এখনই এর কোনও কারণ নেই ... চমৎকার, +1
গাঙ্গনাস

1
"Traditionalতিহ্যবাহী" একক বাস্তবায়নটি দুটি পৃথক শ্রেণীর লোডার এমনকি প্রত্যাশার মতো আচরণ করবে?
রন ক্লেইন

3

এনাম প্যাটার্নটি কোনও শ্রেণীর জন্য ব্যবহার করা যাবে না যা নির্মাণকারীকে ব্যতিক্রম করবে। এটির প্রয়োজন হলে একটি কারখানা ব্যবহার করুন:

class Elvis {
    private static Elvis self = null;
    private int age;

    private Elvis() throws Exception {
        ...
    }

    public synchronized Elvis getInstance() throws Exception {
        return self != null ? self : (self = new Elvis());
    }

    public int getAge() {
        return age;
    }        
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.