ফলব্যাকগুলির জন্য নেস্টেড ট্রাই-ক্যাচের বিকল্পগুলি


14

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

try {
    return repository.getElement(x);
} catch (NotFoundException e) {
    try {
        return repository.getSimilarElement(x);
    } catch (NotFoundException e1) {
        try {
            return repository.getParentElement(x);
        } catch (NotFoundException e2) {
            //can't recover
            throw new IllegalArgumentException(e);
        }
    }
}

এটিকে ভীষণ কুৎসিত দেখাচ্ছে। আমি নাল ফিরতে ঘৃণা করি, তবে এই পরিস্থিতিতে কি এটি আরও ভাল?

Element e = return repository.getElement(x);
if (e == null) {
    e = repository.getSimilarElement(x);
}
if (e == null) {
    e = repository.getParentElement(x);
}
if (e == null) {
    throw new IllegalArgumentException();
}
return e;

অন্য বিকল্প আছে?

নেস্টেড ট্রাই-ক্যাচ ব্লকগুলি কী কোনও অ্যান্টি-প্যাটার্ন ব্যবহার করে? সম্পর্কিত হয়, তবে উত্তরগুলি "কখনও কখনও, তবে এটি সাধারণত এড়ানো যায়" এর পংক্তির সাথে থাকে, কখন বা কীভাবে এড়াতে হবে তা না জানিয়ে।


1
NotFoundExceptionআসলে ব্যতিক্রমী কিছু কি ?

আমি জানি না, এবং সম্ভবত এ কারণেই আমার সমস্যা হচ্ছে। এটি একটি ইকমার্স প্রসঙ্গে, যেখানে পণ্যগুলি প্রতিদিন বন্ধ করা হয়। যদি কেউ এমন কোনও পণ্য বুকমার্ক করে যা পরবর্তীতে বন্ধ হয়ে যায়, এবং তারপরে বুকমার্কটি খোলার চেষ্টা করে ... তা কি ব্যতিক্রম?
অ্যালেক্স উইটগি এগ্রি

@ ফাইভাইন আমার মতে, অবশ্যই না - এটি প্রত্যাশিত। স্ট্যাকওভারফ্লো.com
29২৯299

উত্তর:


17

বাসা বাঁধার স্বাভাবিক উপায় হ'ল ফাংশনগুলি ব্যবহার করা:

Element getElement(x) {
    try {
        return repository.getElement(x);
    } catch (NotFoundException e) {
        return fallbackToSimilar(x);
    }  
}

Element fallbackToSimilar(x) {
    try {
        return repository.getSimilarElement(x);
     } catch (NotFoundException e1) {
        return fallbackToParent(x);
     }
}

Element fallbackToParent(x) {
    try {
        return repository.getParentElement(x);
    } catch (NotFoundException e2) {
        throw new IllegalArgumentException(e);
    }
}

যদি এই ফ্যালব্যাক বিধিগুলি সর্বজনীন হয় তবে আপনি এটিকে সরাসরি repositoryঅবজেক্টে প্রয়োগ করার বিষয়টি বিবেচনা করতে পারেন , যেখানে আপনি ifব্যতিক্রমের পরিবর্তে কেবল সাধারণ বিবৃতি ব্যবহার করতে সক্ষম হতে পারেন ।


1
এই প্রসঙ্গে, methodচেয়ে ভাল শব্দ হবে function
সুলতান

12

অপশন মোনাডের মতো কিছু দিয়ে এটি সত্যিই সহজ হবে। দুর্ভাগ্যক্রমে, জাভা এগুলি নেই। স্কালায় আমি প্রথম সফল সমাধানটি খুঁজে পেতে Tryটাইপটি ব্যবহার করব ।

আমার কার্যনির্বাহী-প্রোগ্রামিং মানসিকতায় আমি বিভিন্ন সম্ভাব্য উত্সের প্রতিনিধিত্বকারী কলব্যাকের একটি তালিকা প্রস্তুত করতাম এবং যতক্ষণ না আমরা প্রথম সফলটিকে খুঁজে পাই ততক্ষণ সেগুলি লুপ করব:

interface ElementSource {
    public Element get();
}

...

final repository = ...;

// this could be simplified a lot using Java 8's lambdas
List<ElementSource> sources = Arrays.asList(
    new ElementSource() {
        @Override
        public Element get() { return repository.getElement(); }
    },
    new ElementSource() {
        @Override
        public Element get() { return repository.getSimilarElement(); }
    },
    new ElementSource() {
        @Override
        public Element get() { return repository.getParentElement(); }
    }
);

Throwable exception = new NoSuchElementException("no sources set up");
for (ElementSource source : sources) {
    try {
        return source.get();
    } catch (NotFoundException e) {
        exception = e;
    }
}
// we end up here if we didn't already return
// so throw the last exception
throw exception;

আপনার যদি সত্যিই বিপুল সংখ্যক উত্স থাকে বা রানটাইম সময় উত্সগুলি কনফিগার করতে হয় তবেই এটি সুপারিশ করা যেতে পারে। অন্যথায়, এটি একটি অপ্রয়োজনীয় বিমূর্ততা এবং আপনি আপনার কোডটি সহজ এবং বোকা রাখার থেকে আরও বেশি লাভ করতে চাইবেন এবং কেবল সেই কুৎসিত নেস্টেড ট্রাই-ক্যাচগুলি ব্যবহার করুন।


Tryস্কালায় টাইপের উল্লেখ করার জন্য, মনাদদের উল্লেখ করার জন্য এবং লুপ ব্যবহার করে সমাধানের জন্য +1 ।
জর্জিও

আমি জাভা 8 এ থাকলে ইতিমধ্যে আমি এর জন্য যাব, তবে আপনি যা বলছেন, এটি কেবল কয়েকটি ফলব্যাকের জন্য কিছুটা।
অ্যালেক্স উইটইগ

1
আসলে, সময় দ্বারা এই উত্তর পোস্ট করা হয়েছে, জাভা 8 সমর্থনে Optionalএকসংখ্যা ( প্রমাণ ) ইতিমধ্যে মুক্তি পায়।
এমকালকভ

3

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

public class TolerantRepository implements SomeKindOfRepositoryInterfaceHopefully {

    private Repository repo;

    public TolerantRepository( Repository r ) {
        this.repo = r;
    }

    public SomeType getElement( SomeType x ) {
        try {
            return this.repo.getElement(x);
        }
        catch (NotFoundException e) {
            /* For example */
            return null;
        }
    }

    // and the same for other methods...

}

3

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

  • "ইউনিট" বা "রিটার্ন" ফাংশনটি ক্লাস কনস্ট্রাক্টর

  • "বাইন্ড" অপারেশন সংকলন সময়ে ঘটে, তাই এটি অনুরোধ থেকে লুকানো

  • "ক্রিয়া" ফাংশনগুলিও সংকলনের সময় ক্লাসের সাথে আবদ্ধ

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

এই বিবেচনার সাথে, মোনাড একটি সাবলীল র‌্যাপার ক্লাসে অনুবাদ করে (যদিও আপনি অনেকটা নমনীয়তা ছেড়ে দিচ্ছেন যা আপনি একটি সম্পূর্ণরূপে কার্যকরী ভাষায় পাবেন):

public class RepositoryLookup<E> {
    private String source;
    private E answer;
    private Exception exception;

    public RepositoryLookup<E>(String source) {
        this.source = source;
    }

    public RepositoryLookup<E> fetchElement() {
        if (answer != null) return this;
        if (! exception instanceOf NotFoundException) return this;

        try {
            answer = lookup(source);
        }
        catch (Exception e) {
            exception = e;
        }

        return this;
    }

    public RepositoryLookup<E> orFetchSimilarElement() {
        if (answer != null) return this; 
        if (! exception instanceOf NotFoundException) return this;

        try {
            answer = lookupVariation(source);
        }
        catch (Exception e) {
            exception = e;
        }

        return this;
    }

    public RepositoryLookup<E> orFetchParentElement() {
        if (answer != null) return this; 
        if (! exception instanceOf NotFoundException) return this;

        try {
            answer = lookupParent(source);
        }
        catch (Exception e) {
            exception = e;
        }

        return this;
    }

    public boolean failed() {
        return exception != null;
    }

    public Exception getException() {
        return exception;
    }

    public E getAnswer() {
        // better to check failed() explicitly ;)
        if (this.exception != null) {
            throw new IllegalArgumentException(exception);
        }
        // TODO: add a null check here?
        return answer;
    }
}

(এটি সংকলন করবে না ... নমুনাকে ছোট রাখার জন্য নির্দিষ্ট বিশদটি অসম্পূর্ণ রেখে দেওয়া হয়েছে)

এবং অনুরোধটি দেখতে এই রকম হবে:

Repository<String> repository = new Repository<String>(x);
repository.fetchElement().orFetchParentElement().orFetchSimilarElement();

if (repository.failed()) {
    throw new IllegalArgumentException(repository.getException());
}

System.err.println("Got " + repository.getAnswer());

নোট করুন যে আপনার পছন্দমতো "আনতে" ক্রিয়াকলাপ রচনার জন্য আপনার নমনীয়তা রয়েছে। কোনও উত্তর পাওয়া না গেলে বা এটি ব্যতীত অন্য কোনও ব্যতিক্রম পেলে এটি থামবে।

আমি সত্যিই এটি দ্রুত করেছি; এটি একেবারেই সঠিক নয়, তবে আশাবাদী ধারণাটি প্রকাশ করেছেন


1
repository.fetchElement().fetchParentElement().fetchSimilarElement();- আমার মতে: অশুভ কোড (জন স্কিটির প্রদত্ত অর্থে)
কনরাড মোরাওস্কি

কিছু লোক সেই স্টাইল পছন্দ করে না, তবে return thisচেইন অবজেক্ট কলগুলি তৈরি করতে ব্যবহার করা দীর্ঘকাল ধরে ছিল। যেহেতু OO পরিবর্তনীয় অবজেক্টগুলিকে জড়িত তাই শৃঙ্খলা ছাড়াই return thisকমবেশি সমান return null। যাইহোক, return new Thing<E>অন্য একটি সামর্থ্যের দ্বার উন্মুক্ত করে যা এই উদাহরণটি প্রবেশ করে না, সুতরাং আপনি যদি এই পথে যেতে চান তবে এই ধরণের পক্ষে এটি গুরুত্বপূর্ণ।
রব

1
তবে আমি সেই স্টাইলটি পছন্দ করি এবং আমি চেইন কল বা সাবলীল ইন্টারফেসের বিরুদ্ধে নই। তবে CustomerBuilder.withName("Steve").withID(403)এই কোড এবং এই কোডের মধ্যে পার্থক্য রয়েছে , কারণ এটি দেখার পরে .fetchElement().fetchParentElement().fetchSimilarElement()কী ঘটেছিল তা পরিষ্কার নয় এবং এটি এখানে মূল বিষয়। তারা সবাই কি আনা হয়? এটি এক্ষেত্রে জমে ওঠে না, এবং অতএব এটি স্বজ্ঞাত নয়। if (answer != null) return thisসত্যই এটি পাওয়ার আগে আমার এটি দেখতে হবে। সম্ভবত এটি যথাযথ নামকরণের বিষয় ( orFetchParent) তবে এটি যাইহোক "যাদু"।
কনরাড মোরাউসকি

1
যাইহোক (আমি জানি আপনার কোড oversimplified হয় এবং মাত্র ধারণার একটি প্রমাণ), এটা ভাল হবে হয়তো এর ক্লোন ফিরে যাওয়ার answerমধ্যে getAnswerএবং রিসেট (স্পষ্ট) answerক্ষেত্র নিজেই এর মান ফেরার আগে। অন্যথায় এটি কমান্ড / ক্যোয়ারী বিচ্ছেদের নীতিটি ভঙ্গ করে, কারণ কোনও উপাদান আনতে জিজ্ঞাসা করা (অনুসন্ধান করা) আপনার রিপোজিটরি অবজেক্টের অবস্থার পরিবর্তন করে ( answerকখনই পুনরায় সেট করা হয় না) এবং fetchElementআপনি পরের বার যখন কল করেন তখন তার আচরণকে প্রভাবিত করে । হ্যাঁ আমি কিছুটা নিটপিক করছি, আমি মনে করি উত্তরটি কার্যকর, আমি এটিকে হ্রাস করিনি।
কনরাড মোরাউসকি

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

2

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

Element e = null;

try {
    e = repository.getElement(x);
} catch (NotFoundException e) {
    // nope -- try again!
}

if (e == null) {  // or ! optionalElement.isPresent()
    try {
        return repository.getSimilarElement(x);
    } catch (NotFoundException e1) {
        // nope -- try again!
    }
}

if (e == null) {  // or ! optionalElement.isPresent()
    try {
        return repository.getParentElement(x);
    } catch (NotFoundException e2) {
        // nope -- try again!
    }
}

if (e == null) {  // or ! optionalElement.isPresent()
    //can't recover
    throw new IllegalArgumentException(e);
}

return e;

এইভাবে, আপনি উপাদানটির অবস্থা দেখছেন, এবং তার অবস্থার উপর ভিত্তি করে সঠিক কল করছেন - এটি যতক্ষণ না আপনার কাছে এখনও উত্তর নেই।

(যদিও আমি @ amon এর সাথে একমত, আমি একটি মোনাড প্যাটার্নটি দেখার পরামর্শ দিচ্ছি, যার মতো একটি মোড়কের জিনিস class Repository<E>রয়েছে যার সদস্য রয়েছে E answer;এবং Exception error;প্রতিটি পর্যায়ে একটি ব্যতিক্রম আছে কিনা তা পরীক্ষা করে দেখুন, এবং যদি থাকে, তবে অবশিষ্ট প্রতিটি পদক্ষেপ এড়িয়ে যান। এ শেষে, আপনি উত্তর, উত্তরের অনুপস্থিতি, বা একটি ব্যতিক্রম নিয়ে বাকি রয়েছেন এবং আপনি কী করতে হবে তা সিদ্ধান্ত নিতে পারেন))


-2

প্রথমত, আমার কাছে মনে হয় এর মতো কোনও অনুষ্ঠান হওয়া উচিত repository.getMostSimilar(x) হওয়া উচিত (আপনার আরও উপযুক্ত নামটি বেছে নেওয়া উচিত) কারণ সেখানে যুক্তি বলে মনে হয় যা কোনও নির্দিষ্ট উপাদানের নিকটবর্তী বা সর্বাধিক অনুরূপ উপাদানের সন্ধান করতে ব্যবহৃত হয়।

সংগ্রহশালাটি অ্যামনো পোস্টে দেখানো মত যুক্তি প্রয়োগ করতে পারে। তার মানে, কেবলমাত্র ব্যতিক্রম ছোঁড়াতে হবে যখন কোনও একক উপাদান পাওয়া যায় নি।

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


উত্তরগুলি
gnat

ঠিক আছে, আমার উত্তরটি তার সমস্যার সমাধান করছে কারণ এটি নির্দিষ্ট শর্তে নেস্টেড চেষ্টা / ক্যাচ এড়ানোর উপায় দেখায়। এই শর্তগুলি না মানলে কেবল আমাদের আরও তথ্যের প্রয়োজন। কেন এটি বৈধ উত্তর নয়?
ভ্যালেনটারি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.