জাভাতে কি সি ++ 'বন্ধু' ধারণাটি অনুকরণ করার কোনও উপায় আছে?


196

আমি একটি প্যাকেজে জাভা ক্লাস লিখতে সক্ষম হতে চাই যা অন্য শ্রেণীর সাবক্লাস না করে অন্য প্যাকেজে কোনও শ্রেণীর অ-সর্বজনীন পদ্ধতিতে অ্যাক্সেস করতে পারে। এটা কি সম্ভব?

উত্তর:


466

এখানে আমি একটি ছোট ট্রিকস যা আমি জাভাতে সি ++ বন্ধুতন্ত্রের প্রতিরূপ তৈরি করতে ব্যবহার করি।

বলি আমার একটি ক্লাস Romeoএবং অন্য ক্লাস রয়েছে Juliet। তারা বিদ্বেষের কারণে বিভিন্ন প্যাকেজে (পরিবার) থাকে।

Romeoচায় cuddle Julietএবং Julietকেবল তাকেই দিতে চায় Romeo cuddle

সি ++ এ, (প্রেমিক) হিসাবে Julietঘোষণা করবে তবে জাভাতে এ জাতীয় কোনও জিনিস নেই।Romeofriend

এখানে ক্লাস এবং কৌশলটি রয়েছে:

মহিলারা প্রথমে :

package capulet;

import montague.Romeo;

public class Juliet {

    public static void cuddle(Romeo.Love love) {
        Objects.requireNonNull(love);
        System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
    }

}

সুতরাং পদ্ধতি Juliet.cuddleহল publicকিন্তু আপনি একটি প্রয়োজন Romeo.Loveএকে ডাকতে। এটি Romeo.Loveএইটিকে "স্বাক্ষর সুরক্ষা" হিসাবে ব্যবহার করে কেবলমাত্র Romeoএই পদ্ধতিটিকে কল করতে পারে এবং প্রেমটি আসল NullPointerExceptionকিনা তা যাচাই করে যাতে রানটাইমটি যদি হয় তবে তা ছোঁড়ে null

এখন ছেলেরা:

package montague;

import capulet.Juliet;

public class Romeo {
    public static final class Love { private Love() {} }
    private static final Love love = new Love();

    public static void cuddleJuliet() {
        Juliet.cuddle(love);
    }
}

ক্লাসটি Romeo.Loveসর্বজনীন তবে এর নির্মাতা private। সুতরাং যে কেউ এটিকে দেখতে পাবে তবে কেবল Romeoএটি তৈরি করতে পারে। আমি একটি স্থিতিশীল রেফারেন্স ব্যবহার করি যাতে এটি Romeo.Loveকখনই ব্যবহৃত হয় না শুধুমাত্র একবারেই নির্মিত হয় এবং অপ্টিমাইজেশনের উপর প্রভাব ফেলে না।

অতএব, Romeoকরতে পারেন cuddle Julietএবং একমাত্র তিনি কারণ সে গঠন করা ও অ্যাক্সেস করতে পারবেন পারেন Romeo.Loveউদাহরণস্বরূপ, যার দ্বারা প্রয়োজন বোধ করা হয় Julietথেকে cuddleতার (অথবা অন্য তিনি একটি সঙ্গে চড় করব NullPointerException)।


107
"আপনাকে একটি নালপয়েন্টারএক্সসেপশন দিয়ে চড় মার" এর জন্য +1 + খুব চিত্তাকর্ষক.
নিকোলাস

2
@ স্টেজি রয়েছে: নটনুল, নননুল এবং চেকফোর নলের টীকাগুলি সন্ধান করুন। কীভাবে সেই টীকাগুলি ব্যবহার এবং প্রয়োগ করতে হবে তা জানতে আপনার আইডিইয়ের ডকুমেন্টেশন দিয়ে দেখুন। আমি জানি যে ইন্টেলিজি এটি ডিফল্টরূপে এম্বেড করে এবং সেই গ্রহনের জন্য একটি প্লাগইন (যেমন ফাইন্ডব্যাগস) প্রয়োজন।
সালমন বিআরওয়াইস

27
আপনি করতে পারেন Romeos 'এর Loveজন্য Juliaপরিবর্তন করে অনন্ত loveক্ষেত্র হতে final;-)।
ম্যাথিয়া

5
@ মাথিয়াস প্রেমের ক্ষেত্রটি স্থির ... আমি এটিকে চূড়ান্ত করার জন্য উত্তরটি সম্পাদনা করব;)
সালমোন বিআরআইএস

12
সমস্ত উত্তর হিউমার এবং দুর্দান্ত উদাহরণের জন্য এই (Y) +1 এর মতো হওয়া উচিত।
জিয়া উল রেহমান মোগল

54

জাভা ডিজাইনাররা বন্ধুত্বের ধারণাটি C ++ এ কাজ করার কারণে স্পষ্টভাবে প্রত্যাখ্যান করেছিল। আপনি আপনার "বন্ধুরা" একই প্যাকেজে রেখেছেন। ভাষা, নকশার অংশ হিসাবে ব্যক্তিগত, সুরক্ষিত এবং প্যাকেজযুক্ত সুরক্ষা প্রয়োগ করা হয়েছে।

জেমস গোসলিং চেয়েছিলেন জাভাটি ভুল ছাড়াই সি ++ হওয়া উচিত। আমি বিশ্বাস করি যে তিনি বন্ধুটিকে ভুল বলে মনে করেছিলেন কারণ এটি ওওপি নীতিগুলি লঙ্ঘন করে। প্যাকেজগুলি ওওপি সম্পর্কে খুব খাঁটি না হয়ে উপাদানগুলি সংগঠিত করার যুক্তিসঙ্গত উপায় সরবরাহ করে।

এনআর উল্লেখ করেছে যে আপনি প্রতিবিম্ব ব্যবহার করে প্রতারণা করতে পারেন, তবে এটি কেবলমাত্র যদি আপনি সিকিউরিটি ম্যানেজার ব্যবহার না করেন তবে কাজ করে। আপনি যদি জাভা স্ট্যান্ডার্ড সুরক্ষা চালু করেন তবে আপনি সুনির্দিষ্টভাবে অনুমতি দেওয়ার জন্য সুরক্ষা নীতি না লিখলে আপনি প্রতিচ্ছবি দিয়ে প্রতারণা করতে পারবেন না।


11
আমি প্যাডেন্ট হতে চাই না, তবে অ্যাক্সেস মডিফায়ারগুলি কোনও সুরক্ষা ব্যবস্থা নয়।
গ্রেগ ডি

6
অ্যাক্সেস মডিফায়ারগুলি জাভা সুরক্ষা মডেলের অংশ। আমি বিশেষভাবে প্রতিবিম্বের জন্য জাভা.লং.রুনটাইমপ্রেমিশনটির কথা উল্লেখ করছিলাম: অ্যাক্সেসডেক্লেয়ারড মেম্বার্স এবং অ্যাক্সেসক্লাসইনপ্যাকেজ।
ডেভিড জি

54
গোসলিং যদি সত্যিই ভাবেন যে friendওওপি লঙ্ঘন করেছে (বিশেষত, প্যাকেজ অ্যাক্সেসের চেয়ে বেশি) তবে সে সত্যই তা বুঝতে পারে না (সম্পূর্ণ সম্ভব, অনেক লোক এটি ভুল বোঝে )।
কনরাড রুডল্ফ

8
শ্রেণীর উপাদানগুলি কখনও কখনও পৃথক করা প্রয়োজন (যেমন বাস্তবায়ন এবং এপিআই, কোর অবজেক্ট এবং অ্যাডাপ্টার)। প্যাকেজ-স্তরের সুরক্ষা একই সময়ে খুব অনুমতিপ্রসূত এবং এটি সঠিকভাবে করতে খুব বেশি নিষেধাজ্ঞ।
ধর্মি

2
@ গ্রেগডি এগুলি এই সুরে সুরক্ষা ব্যবস্থা হিসাবে বিবেচনা করা যেতে পারে যে তারা বিকাশকারীদের কোনও শ্রেণীর সদস্যকে ভুলভাবে ব্যবহার করা থেকে বিরত রাখতে সহায়তা করে। আমি মনে করি তারা সম্ভবত সুরক্ষা ব্যবস্থা হিসাবে উল্লেখ করা চাই ।
ক্রাশ

45

জাভাতে 'বন্ধু' ধারণাটি কার্যকর, উদাহরণস্বরূপ, এর প্রয়োগ থেকে একটি এপিআই আলাদা করতে। প্রয়োগকারী ক্লাসগুলির জন্য এপিআই শ্রেণির অভ্যন্তরীণ প্রবেশাধিকার প্রয়োজন তবে এটি এপিআই ক্লায়েন্টের কাছে প্রকাশ করা উচিত নয়। নীচে বিস্তারিত হিসাবে 'বন্ধু অ্যাকসেসর' প্যাটার্নটি ব্যবহার করে এটি অর্জন করা যেতে পারে:

ক্লাসটি API এর মাধ্যমে উন্মুক্ত:

package api;

public final class Exposed {
    static {
        // Declare classes in the implementation package as 'friends'
        Accessor.setInstance(new AccessorImpl());
    }

    // Only accessible by 'friend' classes.
    Exposed() {

    }

    // Only accessible by 'friend' classes.
    void sayHello() {
        System.out.println("Hello");
    }

    static final class AccessorImpl extends Accessor {
        protected Exposed createExposed() {
            return new Exposed();
        }

        protected void sayHello(Exposed exposed) {
            exposed.sayHello();
        }
    }
}

ক্লাসটি 'বন্ধু' কার্যকারিতা সরবরাহ করে:

package impl;

public abstract class Accessor {

    private static Accessor instance;

    static Accessor getInstance() {
        Accessor a = instance;
        if (a != null) {
            return a;
        }

        return createInstance();
    }

    private static Accessor createInstance() {
        try {
            Class.forName(Exposed.class.getName(), true, 
                Exposed.class.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }

        return instance;
    }

    public static void setInstance(Accessor accessor) {
        if (instance != null) {
            throw new IllegalStateException(
                "Accessor instance already set");
        }

        instance = accessor;
    }

    protected abstract Exposed createExposed();

    protected abstract void sayHello(Exposed exposed);
}

'বন্ধু' বাস্তবায়ন প্যাকেজে ক্লাস থেকে অ্যাক্সেসের উদাহরণ:

package impl;

public final class FriendlyAccessExample {
    public static void main(String[] args) {
        Accessor accessor = Accessor.getInstance();
        Exposed exposed = accessor.createExposed();
        accessor.sayHello(exposed);
    }
}

1
কারণ আমি জানতাম না যে "এক্সপোজড" শ্রেণিতে "স্ট্যাটিক" অর্থ কী: স্ট্যাটিক ব্লক, জাভা ক্লাসের অভ্যন্তরের স্টেটিকের
গাই এল

আকর্ষণীয় নিদর্শন তবে এর জন্য এক্সপোজড এবং অ্যাকসেসর ক্লাসগুলি সর্বজনীন হওয়া দরকার যখন একটি এপিআই প্রয়োগকারী ক্লাসগুলির (যেমন, জাভা ক্লাসগুলির একটি সেট পাবলিক জাভা ইন্টারফেসের সেট প্রয়োগ করে) আরও ভাল "ডিফল্ট সুরক্ষিত" হবে এবং এইভাবে ক্লায়েন্টের অ্যাক্সেসযোগ্য তাদের প্রয়োগ থেকে পৃথক পৃথক করতে।
ইয়ান-গাল গুহানিয়ুক

8
আমি আমার জাভাতে মোটামুটি মরিচা, তাই আমার অজ্ঞতা ক্ষমা করুন। "রোমিও এবং জুলিয়েট" সমাধান সালমন বিআরওয়াইএস পোস্ট করে এর কী সুবিধা? এই প্রয়োগটি আমার প্যান্টগুলি ভীতি প্রদর্শন করবে যদি আমি কোনও কোড বেসে এটির উপর হোঁচট খায় (আপনার ব্যাখ্যা সংযুক্ত না করে, অর্থাৎ ভারী মন্তব্য না করা)। রোমিও এবং জুলিয়েট পদ্ধতির বিষয়টি বুঝতে খুব সহজ।
স্টিজি

1
এই পদ্ধতির সমস্যাগুলি কেবল রানটাইমে দৃশ্যমান করে তুলবে, অন্যদিকে বিকাশকালে রোমিও এবং জুলিয়েট অপব্যবহারগুলি তাদের সংকলন সময়ে দৃশ্যমান করে তুলবে।
ymajoros

1
@ ইয়ামাজোরাস দ্য রোমিও এবং জুলিয়েট উদাহরণটি সংকলন-সময় দুর্গতিটিকে দৃশ্যমান করে না। এটি একটি যুক্তি সঠিকভাবে পাস হওয়া এবং একটি ব্যতিক্রম নিক্ষেপ করে rel এগুলি উভয়ই রান-টাইম ক্রিয়া।
রেডিওডেফ

10

আপনার প্রশ্নের দুটি সমাধান রয়েছে যা সমস্ত ক্লাসকে একই প্যাকেজে রাখার সাথে জড়িত না।

প্রথমটি হ'ল (ব্যবহারিক এপিআই ডিজাইন, তুলাক ২০০৮) বর্ণিত ফ্রেন্ড অ্যাকসেসর / ফ্রেন্ড প্যাকেজ প্যাটার্নটি ব্যবহার করা ।

দ্বিতীয়টি হল ওএসজিআই ব্যবহার করা। ওএসজি এটি কীভাবে সম্পাদন করে তা ব্যাখ্যা করে এখানে একটি নিবন্ধ রয়েছে ।

সম্পর্কিত প্রশ্ন: 1 , 2 , এবং 3


7

আমি যতদূর জানি, এটি সম্ভব নয়।

হতে পারে, আপনি আমাদের আপনার নকশা সম্পর্কে আরও কিছু বিবরণ দিতে পারেন। এই জাতীয় প্রশ্নগুলি সম্ভবত নকশার ত্রুটির ফলাফল।

শুধু বিবেচনা করুন

  • এই ক্লাসগুলি কেন বিভিন্ন প্যাকেজের মধ্যে রয়েছে, যদি তারা এতটা ঘনিষ্ঠভাবে সম্পর্কিত হয়?
  • বি এর ব্যক্তিগত সদস্যদের অ্যাক্সেস করার জন্য কি এ বা অপারেশনটি বি ক্লাসে স্থানান্তরিত হওয়া এবং এ দ্বারা ট্রিগার করা উচিত?
  • এটি কি সত্যিই কল করছে বা ইভেন্ট হ্যান্ডলিং আরও ভাল?

3

eirikma এর উত্তর সহজ এবং দুর্দান্ত। আমি আরও একটি জিনিস যুক্ত করতে পারি: জনসাধারণের অ্যাক্সেসযোগ্য পদ্ধতি না থাকার পরিবর্তে, এমন বন্ধুটি পাওয়ার জন্য getFender () ব্যবহার করুন যা আপনি ব্যবহার করতে পারবেন না, আপনি একধাপ এগিয়ে যেতে পারেন এবং টোকেন ছাড়াই বন্ধুটি পেতে অস্বীকার করতে পারেন: getFender (Service.FenderToken)। এই ফ্রেন্ডটোকনটি কোনও প্রাইভেট কনস্ট্রাক্টরের সাথে অভ্যন্তরীণ পাবলিক ক্লাস হবে, যাতে কেবল পরিষেবাটি কোনওটিকে তাত্পর্যপূর্ণ করতে পারে।


3

পুনরায় ব্যবহারযোগ্য Friendশ্রেণীর সাথে এখানে একটি স্পষ্ট ব্যবহারের ক্ষেত্রে উদাহরণ রয়েছে । এই পদ্ধতির সুবিধা হ'ল ব্যবহারের সরলতা। অন্যান্য পরীক্ষার চেয়ে ইউনিট পরীক্ষার ক্লাসগুলি আরও অ্যাক্সেস দেওয়ার জন্য ভাল।

শুরু করার জন্য, এখানে Friendক্লাসটি কীভাবে ব্যবহার করা যায় তার একটি উদাহরণ ।

public class Owner {
    private final String member = "value";

    public String getMember(final Friend friend) {
        // Make sure only a friend is accepted.
        friend.is(Other.class);
        return member;
    }
}

তারপরে অন্য প্যাকেজে আপনি এটি করতে পারেন:

public class Other {
    private final Friend friend = new Friend(this);

    public void test() {
        String s = new Owner().getMember(friend);
        System.out.println(s);
    }
}

Friendবর্গ নিম্নরূপ।

public final class Friend {
    private final Class as;

    public Friend(final Object is) {
        as = is.getClass();
    }

    public void is(final Class c) {
        if (c == as)
            return;
        throw new ClassCastException(String.format("%s is not an expected friend.", as.getName()));
    }

    public void is(final Class... classes) {
        for (final Class c : classes)
            if (c == as)
                return;
        is((Class)null);
    }
}

তবে সমস্যাটি হ'ল এটিকে এভাবে ব্যবহার করা যেতে পারে:

public class Abuser {
    public void doBadThings() {
        Friend badFriend = new Friend(new Other());
        String s = new Owner().getMember(badFriend);
        System.out.println(s);
    }
}

এখন, এটি সত্য হতে পারে যে Otherশ্রেণীর কোনও পাবলিক কনস্ট্রাক্টর নেই, সুতরাং উপরের Abuserকোডটি অসম্ভব করে তোলে। যাইহোক, আপনার বর্গ যদি না একটি পাবলিক কন্সট্রাকটর আছে তারপর, এটা সম্ভবত যুক্তিযুক্ত ইনার শ্রেণী হিসেবে বন্ধুকে বর্গ নকল হয়। এই Other2শ্রেণীর উদাহরণ হিসাবে ধরুন :

public class Other2 {
    private final Friend friend = new Friend();

    public final class Friend {
        private Friend() {}
        public void check() {}
    }

    public void test() {
        String s = new Owner2().getMember(friend);
        System.out.println(s);
    }
}

এবং তারপরে Owner2ক্লাসটি এমন হবে:

public class Owner2 {
    private final String member = "value";

    public String getMember(final Other2.Friend friend) {
        friend.check();
        return member;
    }
}

লক্ষ করুন যে Other2.Friendক্লাসটির একটি বেসরকারী নির্মাণকারী রয়েছে, সুতরাং এটি এটি করার আরও সুরক্ষিত উপায় করে।


2

প্রদত্ত সমাধান সম্ভবত সহজ ছিল না। C ++ এর মত একই ধারণার উপর ভিত্তি করে আরেকটি পদ্ধতি: ব্যক্তিগত সদস্যরা প্যাকেজ / ব্যক্তিগত সুযোগের বাইরে অ্যাক্সেসযোগ্য না, নির্দিষ্ট শ্রেণি ব্যতীত যে মালিক তার নিজের বন্ধু তৈরি করে।

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

কোডটি এখানে:

প্রথম পরীক্ষা যা যাচাই করে যে এটি আসলে কাজ করে:

package application;

import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;

public class EntityFriendTest extends TestCase {
    public void testFriendsAreOkay() {
        Entity entity = new Entity();
        Service service = new Service();
        assertNull("entity should not be processed yet", entity.getPublicData());
        service.processEntity(entity);
        assertNotNull("entity should be processed now", entity.getPublicData());
    }
}

তারপরে পরিষেবাটি যে সত্তার ব্যক্তিগত প্যাকেজের কোনও প্যাকেজের বন্ধু অ্যাক্সেসের প্রয়োজন:

package application.service;

import application.entity.Entity;

public class Service {

    public void processEntity(Entity entity) {
        String value = entity.getFriend().getEntityPackagePrivateData();
        entity.setPublicData(value);
    }

    /**
     * Class that Entity explicitly can expose private aspects to subclasses of.
     * Public, so the class itself is visible in Entity's package.
     */
    public static abstract class EntityFriend {
        /**
         * Access method: private not visible (a.k.a 'friendly') outside enclosing class.
         */
        private String getEntityPackagePrivateData() {
            return getEntityPackagePrivateDataImpl();
        }

        /** contribute access to private member by implementing this */
        protected abstract String getEntityPackagePrivateDataImpl();
    }
}

শেষ অবধি: সত্তা শ্রেণি যা কেবলমাত্র ক্লাস অ্যাপ্লিকেশন.সার্ভিস.সোর্সিতে প্যাকেজ ব্যক্তিগত সদস্যের জন্য বন্ধুত্বপূর্ণ অ্যাক্সেস সরবরাহ করে।

package application.entity;

import application.service.Service;

public class Entity {

    private String publicData;
    private String packagePrivateData = "secret";   

    public String getPublicData() {
        return publicData;
    }

    public void setPublicData(String publicData) {
        this.publicData = publicData;
    }

    String getPackagePrivateData() {
        return packagePrivateData;
    }

    /** provide access to proteced method for Service'e helper class */
    public Service.EntityFriend getFriend() {
        return new Service.EntityFriend() {
            protected String getEntityPackagePrivateDataImpl() {
                return getPackagePrivateData();
            }
        };
    }
}

ঠিক আছে, আমি অবশ্যই এটি স্বীকার করতে হবে "বন্ধু পরিষেবা :: পরিষেবা" এর চেয়ে কিছুটা দীর্ঘ তবে টীকাগুলি ব্যবহার করে সংকলন-সময় পরীক্ষা চালিয়ে যাওয়ার সময় এটি সংক্ষিপ্ত করা সম্ভব হতে পারে।


এটি একই প্যাকেজে সাধারণ শ্রেণি হিসাবে কেবল ফ্রেন্ড () পেতে পারে এবং এটি ব্যক্তিগতটিকে বাইপাস করে সুরক্ষিত পদ্ধতিতে কল করতে পারে তেমন কাজ করে না।
ব্যবহারকারী2219808

1

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

এই নিয়মটি সর্বদা জানা যায় না এবং এটি একটি সি ++ "বন্ধু" কীওয়ার্ডের একটি ভাল অনুমিতিকরণ। আমি এটি একটি ভাল প্রতিস্থাপন খুঁজে।


1
এটি সত্য, তবে আমি সত্যিই বিভিন্ন প্যাকেজগুলিতে থাকা কোড সম্পর্কে জিজ্ঞাসা করছিলাম ...
ম্যাথু মারডোক

1

আমি মনে করি যে সি ++ এ বন্ধুত্বপূর্ণ ক্লাসগুলি জাভাতে অভ্যন্তর-শ্রেণীর ধারণার মতো। অভ্যন্তরীণ-শ্রেণীর ব্যবহার করে আপনি প্রকৃতপক্ষে একটি বদ্ধ শ্রেণি এবং একটি বদ্ধ শ্রেণীর সংজ্ঞা দিতে পারেন। বদ্ধ ক্লাসের এর ঘেরযুক্ত শ্রেণীর সরকারী এবং ব্যক্তিগত সদস্যদের কাছে সম্পূর্ণ অ্যাক্সেস রয়েছে। নিম্নলিখিত লিঙ্কটি দেখুন: http://docs.oracle.com/javase/torial/java/javaOO/nested.html


এর, না, তারা না। এটি বাস্তব জীবনে বন্ধুত্বের মতো আরও: এটি হতে পারে তবে পারস্পরিক হওয়ার দরকার নেই (এ বি এর বন্ধু হওয়া মানে B কে এ এর ​​বন্ধু হিসাবে বিবেচনা করা হয় না) এবং আপনি এবং আপনার বন্ধুরা একেবারে আলাদা হতে পারেন পরিবারগুলি এবং আপনার নিজস্ব (সম্ভবত প্রয়োজনীয় নয়) ওভারল্যাপিং বন্ধুদের চেনাশোনা রয়েছে। (এমন নয় যে আমি প্রচুর বন্ধুর সাথে ক্লাস দেখতে চাই It এটি একটি দরকারী বৈশিষ্ট্য হতে পারে তবে সাবধানতার সাথে ব্যবহার করা উচিত))
ক্রিস্টোফার ক্রিটজিগ

1

আমি মনে করি, বন্ধুর অ্যাকসেসর প্যাটার্নটি ব্যবহার করার পদ্ধতিটি খুব জটিল। আমাকে একই সমস্যার মুখোমুখি হতে হয়েছিল এবং আমি জাভাতে সি ++ থেকে পরিচিত ভাল, পুরানো অনুলিপি নির্মাণকারী ব্যবহার করে সমাধান করেছি:

public class ProtectedContainer {
    protected String iwantAccess;

    protected ProtectedContainer() {
        super();
        iwantAccess = "Default string";
    }

    protected ProtectedContainer(ProtectedContainer other) {
        super();
        this.iwantAccess = other.iwantAccess;
    }

    public int calcSquare(int x) {
        iwantAccess = "calculated square";
        return x * x;
    }
}

আপনার আবেদনে আপনি নিম্নলিখিত কোডটি লিখতে পারেন:

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

এই পদ্ধতির সুবিধাটি হ'ল কেবলমাত্র আপনার অ্যাপ্লিকেশনটির সুরক্ষিত ডেটাতে অ্যাক্সেস রয়েছে। এটি ঠিক বন্ধু কীওয়ার্ডের প্রতিস্থাপন নয়। তবে আমি মনে করি আপনি কাস্টম লাইব্রেরিগুলি লেখার সময় এটি বেশ উপযুক্ত এবং আপনার সুরক্ষিত ডেটা অ্যাক্সেস করতে হবে।

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

এটি সুরক্ষিত পদ্ধতিগুলির সাথেও কাজ করে। আপনি তাদের আপনার এপিআই এ সুরক্ষিত সংজ্ঞা দিয়েছেন। পরে আপনার আবেদনে আপনি একটি প্রাইভেট র‍্যাপার ক্লাস লিখবেন এবং সুরক্ষিত পদ্ধতিটিকে জনসমক্ষে প্রকাশ করবেন। এটাই.


1
তবে ProtectedContainerপ্যাকেজের বাইরে সাবক্ল্যাস করা যাবে!
রাফেল

0

আপনি যদি সুরক্ষিত পদ্ধতিগুলি অ্যাক্সেস করতে চান তবে আপনি যে শ্রেণীর ব্যবহার করতে চান তার একটি সাবক্লাস তৈরি করতে পারেন যা আপনি পাবলিক হিসাবে ব্যবহার করতে চান এমন পদ্ধতিগুলি (বা নামস্থানের অভ্যন্তরীণ নিরাপদ হতে অভ্যন্তরীণ) প্রকাশ করে এবং আপনার শ্রেণিতে সেই শ্রেণীর উদাহরণ রয়েছে (এটি প্রক্সি হিসাবে ব্যবহার করুন)।

যতদূর ব্যক্তিগত পদ্ধতি সম্পর্কিত (আমার মনে হয়) আপনি ভাগ্যের বাইরে।


0

আমি সম্মত হই যে বেশিরভাগ ক্ষেত্রে বন্ধুর কীওয়ার্ড অপ্রয়োজনীয়।

  • প্যাকেজ-প্রাইভেট (যেমন। ডিফল্ট) বেশিরভাগ ক্ষেত্রেই আপনার পর্যায়ে বেশ জড়িত ক্লাস রয়েছে in
  • অভ্যন্তরীণগুলিতে অ্যাক্সেস চায় এমন ডিবাগ ক্লাসগুলির জন্য, আমি সাধারণত পদ্ধতিটি ব্যক্তিগত করি এবং প্রতিবিম্বের মাধ্যমে এটিকে অ্যাক্সেস করি। গতি সাধারণত এখানে গুরুত্বপূর্ণ নয়
  • কখনও কখনও, আপনি একটি পদ্ধতি প্রয়োগ করেন যা "হ্যাক" বা অন্যথায় যা পরিবর্তিত হতে পারে। আমি এটি সর্বজনীন করে দিই, তবে আপনার বিদ্যমান এই পদ্ধতির উপর নির্ভর করতে হবে না তা বোঝাতে @ অনুগ্রহ করে ব্যবহার করুন।

এবং পরিশেষে, যদি এটি সত্যিই প্রয়োজন হয় তবে অন্যান্য উত্তরগুলিতে উল্লিখিত বন্ধুর অ্যাকসেসর প্যাটার্ন রয়েছে।


0

কোনও কীওয়ার্ড বা ব্যবহার না করে।

আপনি প্রতিবিম্ব ইত্যাদি ব্যবহার করে "প্রতারণা" করতে পারেন তবে আমি "প্রতারণা" করার পরামর্শ দেব না।


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

0

এই সমস্যাটি সমাধান করার জন্য আমি যে পদ্ধতিটি পেয়েছি সেটি হ'ল একটি অ্যাক্সেসর অবজেক্ট তৈরি করা, যেমন:

class Foo {
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* This is the accessor. Anyone with a reference to this has special access. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    /** You get an accessor by calling this method. This method can only
     * be called once, so calling is like claiming ownership of the accessor. */
    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }
}

getAccessor()অ্যাকসেসরের "দাবি মালিকানার" কল করার জন্য প্রথম কোড । সাধারণত, এটি এমন কোড যা বস্তু তৈরি করে।

Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.

সি ++ এর বন্ধুত্বপূর্ণ ব্যবস্থার চেয়েও এটির একটি সুবিধা রয়েছে কারণ এটি আপনাকে প্রতি- স্তরের স্তরের বিপরীতে প্রতি-প্রতি স্তরের স্তরে অ্যাক্সেস সীমাবদ্ধ করতে দেয় । অ্যাক্সেসর রেফারেন্স নিয়ন্ত্রণ করে আপনি অবজেক্টে অ্যাক্সেস নিয়ন্ত্রণ করেন। আপনি একাধিক অ্যাক্সেসর তৈরি করতে পারেন এবং প্রত্যেককে আলাদা আলাদা অ্যাক্সেস দিতে পারেন যা কোন কোডটি কী অ্যাক্সেস করতে পারে তার উপর সূক্ষ্ম-নিয়ন্ত্রণযুক্ত নিয়ন্ত্রণের অনুমতি দেয়:

class Foo {
    private String secret;
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* Normal accessor. Can write to locked, but not read secret. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }

    /* Super accessor. Allows access to secret. */
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    private FooSuperAccessor superAccessor;

    public FooSuperAccessor getAccessor() {
        if (superAccessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return superAccessor = new FooSuperAccessor();
    }
}

অবশেষে, আপনি যদি কিছুটা আরও সংগঠিত হতে চান তবে আপনি একটি রেফারেন্স অবজেক্ট তৈরি করতে পারেন, যা সবকিছুকে একসাথে ধারণ করে। এটি আপনাকে এক মেথড কল দিয়ে সমস্ত অ্যাক্সেসরদের দাবি করতে এবং পাশাপাশি তাদের লিঙ্কযুক্ত উদাহরণের সাথে একত্রে রাখার অনুমতি দেয়। আপনার রেফারেন্সটি একবার হয়ে গেলে আপনি প্রবেশকারীদের কোডের প্রয়োজনে পাস করতে পারেন:

class Foo {
    private String secret;
    private String locked;

    public String getLocked() { return locked; }

    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    public class FooReference {
        public final Foo foo;
        public final FooAccessor accessor;
        public final FooSuperAccessor superAccessor;

        private FooReference() {
            this.foo = Foo.this;
            this.accessor = new FooAccessor();
            this.superAccessor = new FooSuperAccessor();
        }
    }

    private FooReference reference;

    /* Beware, anyone with this object has *all* the accessors! */
    public FooReference getReference() {
        if (reference != null)
            throw new IllegalStateException("Cannot return reference more than once!");
        return reference = new FooReference();
    }
}

অনেকটা মাথা ঠেকানোর পরে (ভাল ধরণের নয়), এটি আমার চূড়ান্ত সমাধান ছিল এবং আমি এটি খুব পছন্দ করি। এটি নমনীয়, ব্যবহার করা সহজ এবং শ্রেণি অ্যাক্সেসের উপর খুব ভাল নিয়ন্ত্রণের অনুমতি দেয়। ( কেবলমাত্র রেফারেন্স সহ অ্যাক্সেসটি খুব কার্যকর)) আপনি যদি অ্যাক্সেসর / রেফারেন্সগুলির জন্য ব্যক্তিগত পরিবর্তে সুরক্ষিত ব্যবহার করেন তবে ফু এর উপ-শ্রেণিগুলি এমনকি এর থেকে প্রসারিত রেফারেন্সও ফিরিয়ে দিতে পারে getReference। এটিতে কোনও প্রতিচ্ছবি প্রয়োজন হয় না, সুতরাং এটি যে কোনও পরিবেশে ব্যবহার করা যেতে পারে।


0

জাভা 9 হিসাবে, মডিউলগুলি অনেক ক্ষেত্রে এটি একটি নন-ইস্যু হিসাবে তৈরি করতে ব্যবহার করা যেতে পারে।


0

আমি এটিকে পাবলিক ক্লাস না করে এড়াতে প্রতিনিধি বা সংমিশ্রণ বা কারখানার শ্রেণি (এই সমস্যাটির ফলাফলের উপর নির্ভর করে) পছন্দ করি।

যদি এটি "বিভিন্ন প্যাকেজগুলিতে ইন্টারফেস / বাস্তবায়ন ক্লাস" সমস্যা হয় তবে আমি এমন একটি পাবলিক ফ্যাক্টরি ক্লাস ব্যবহার করব যা ইমপ্লের প্যাকেজের মতো একই প্যাকেজে থাকবে এবং ইমপ্লের ক্লাসের এক্সপোজার প্রতিরোধ করবে।

যদি এটি "অন্য শ্রেণীর জন্য অন্য কোনও প্যাকেজের জন্য এই কার্যকারিতাটি সরবরাহ করার জন্য আমি এই শ্রেণি / পদ্ধতিটিকে জনসাধারণ্যে ঘৃণা করি" তবে আমি একই প্যাকেজে একজন পাবলিক প্রতিনিধি শ্রেণি ব্যবহার করব এবং কার্যকারিতার কেবলমাত্র সেই অংশটিই প্রকাশ করব would "বহিরাগত" শ্রেণীর দ্বারা প্রয়োজনীয়।

এর মধ্যে কয়েকটি সিদ্ধান্ত লক্ষ্য সার্ভারের ক্লাসলোডিং আর্কিটেকচার (ওএসজিআই বান্ডেল, ওয়ার / ইএআর, ইত্যাদি), স্থাপনা এবং প্যাকেজ নামকরণ কনভেনশন দ্বারা চালিত হয়। উদাহরণস্বরূপ, উপরের প্রস্তাবিত সমাধান, 'ফ্রেন্ড অ্যাকসেসর' প্যাটার্নটি সাধারণ জাভা অ্যাপ্লিকেশনগুলির জন্য চালাক। ক্লাসলোডিং শৈলীর পার্থক্যের কারণে এটি OSGi এ বাস্তবায়ন করা যদি কঠিন হয়ে যায় তবে আমি ভাবছি।


0

এটি কারওর উপকারে আছে কিনা তা আমি জানি না তবে আমি এটি নিম্নলিখিত পদ্ধতিতে পরিচালনা করেছি:

আমি একটি ইন্টারফেস তৈরি করেছি (অ্যাডমিনাইট)।

ফাংশনগুলিতে কল করতে সক্ষম হওয়া উচিত এমন প্রতিটি শ্রেণীর অ্যাডমিনরাটগুলি প্রয়োগ করা উচিত।

তারপরে আমি নিম্নরূপে একটি ফাংশন HasAdminRights তৈরি করেছি:

private static final boolean HasAdminRights()
{
    // Gets the current hierarchy of callers
    StackTraceElement[] Callers = new Throwable().getStackTrace();

    // Should never occur with me but if there are less then three StackTraceElements we can't check
    if (Callers.length < 3)
    {
        EE.InvalidCode("Couldn't check for administrator rights");
        return false;

    } else try
    {

        // Now we check the third element as this function is the first and the function wanting to check for the rights the second. We try to use it as a subclass of AdminRights.
        Class.forName(Callers[2].getClassName()).asSubclass(AdminRights.class);

        // If everything worked up to now, it has admin rights!
        return true;

    } catch (java.lang.ClassCastException | ClassNotFoundException e)
    {
        // In the catch, something went wrong and we can deduce that the caller has no admin rights

        EE.InvalidCode(Callers[1].getClassName() + " doesn't have administrator rights");
        return false;
    }
}

-1

আমি একবার প্রতিচ্ছবি ভিত্তিক সমাধান দেখেছি যা রানটাইমে প্রতিবিম্ব ব্যবহার করে "বন্ধু চেকিং" করেছিল এবং পদ্ধতি কলকারী শ্রেণিটিকে এটি করার অনুমতি দেওয়া হয়েছে কিনা তা দেখার জন্য কল স্ট্যাক পরীক্ষা করে। রানটাইম চেক হওয়ায় এটির স্পষ্টতই কমতি রয়েছে।

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