कोड डिजाइन: मनमाने कार्यों का प्रतिनिधिमंडल


9

पीपीसीजी पर, हमारे पास अक्सर किंग ऑफ द हिल चुनौतियां होती हैं, जो एक-दूसरे के खिलाफ अलग-अलग कोड बॉट पिट करते हैं। हमें इन चुनौतियों को एक भाषा में सीमित करना पसंद नहीं है, इसलिए हम मानक I / O पर क्रॉस-प्लेटफॉर्म संचार करते हैं।

मेरा लक्ष्य एक ऐसी रूपरेखा लिखना है जिसे चुनौती देने वाले लेखक इन चुनौतियों को आसान बनाने के लिए उपयोग कर सकेंगे। मैं निम्नलिखित आवश्यकताओं के साथ आया हूं जिन्हें मैं पूरा करना चाहता हूं:

  1. चुनौती-लेखक एक वर्ग बनाने में सक्षम है जहां विधियां प्रत्येक अलग-अलग संचार का प्रतिनिधित्व करती हैं । उदाहरण के लिए, हमारी अच्छी बनाम बुराई की चुनौती पर, लेखक एक ऐसा Playerवर्ग बनाएगा, जिस abstract boolean vote(List<List<Boolean>> history)पर एक विधि हो।

  2. नियंत्रक उपरोक्त वर्ग के उदाहरणों को प्रदान करने में सक्षम है जो मानक I / O के माध्यम से संचार करते हैं जब aformentioned विधियों को बुलाया जाता है । उस ने कहा, उपरोक्त कक्षा के सभी उदाहरण अनिवार्य रूप से मानक I / O पर संवाद नहीं करेंगे। बॉट्स में से 3 देशी जावा बॉट्स हो सकते हैं (जो कि केवल Playerकक्षा को ओवरराइड करते हैं , जहाँ एक और 2 अन्य भाषा में हैं)

  3. विधियों में हमेशा समान संख्या में तर्क नहीं होंगे (न ही उनका हमेशा रिटर्न मान होगा)

  4. मैं चुनौती-लेखक को अपने ढांचे के साथ काम करने के लिए जितना संभव हो उतना कम काम करना चाहता हूं।

मैं इन समस्याओं को हल करने के लिए प्रतिबिंब का उपयोग करने के खिलाफ नहीं हूं। मैंने चुनौती देने वाले लेखक को कुछ करने की आवश्यकता पर विचार किया है:

class PlayerComm extends Player {
    private Communicator communicator;
    public PlayerComm(Communicator communicator){
        this.communicator = communicator;
    }
    @Override
    boolean vote(List<List<Boolean>> history){
         return (Boolean)communicator.sendMessage(history);
    }
}

लेकिन अगर कई तरीके हैं, तो यह बहुत दोहराव प्राप्त कर सकता है, और निरंतर कास्टिंग मजेदार नहीं है। ( sendMessageइस उदाहरण में एक चर संख्या के Objectतर्क स्वीकार किए जाएंगे , और वापस आ जाएंगे Object)

क्या ऐसा करने के लिए इससे अच्छा तरीका है?


1
मैं " PlayerComm extends Player" -पार्ट के बारे में उलझन में हूँ । क्या सभी जावा प्रवेशकों का विस्तार हो रहा है Player, और यह PlayerCommवर्ग गैर-जावा प्रवेशकों के लिए एक एडेप्टर है?
ज़ीरोएन

हां, यह सही है
नाथन मेरिल

तो, जिज्ञासा से बाहर ... क्या आपने इसके लिए किसी तरह के अच्छे समाधान के साथ आने का प्रबंधन किया?
जीरोऑन

नहीं। मुझे नहीं लगता कि मैं जो चाहता हूं वह जावा में संभव है: /
नाथन मेरिल

जवाबों:


1

ठीक है, इस तरह की चीजें बढ़ी और मैं निम्नलिखित दस वर्गों के साथ समाप्त हुआ ...

इस विधि में लब्बोलुआब यह है कि सभी संचार Messageवर्ग का उपयोग करते हुए होता है , अर्थात खेल कभी भी खिलाड़ियों के तरीकों को सीधे नहीं कहता है लेकिन हमेशा आपके ढांचे से संचारक वर्ग का उपयोग करता है। देशी जावा कक्षाओं के लिए एक प्रतिबिंब-आधारित संचारक है और फिर सभी गैर-जावा खिलाड़ियों के लिए एक कस्टम संचारक होना चाहिए। Message<Integer> message = new Message<>("say", Integer.class, "Hello");एक संदेश को शुरू करने के लिए एक sayपैरामीटर नाम के साथ संदेश को "Hello"लौटा सकता है a Integer। इसके बाद एक संचारक (खिलाड़ी प्रकार के आधार पर कारखाने का उपयोग करके उत्पन्न) को पारित किया जाता है, जो तब कमांड को निष्पादित करता है।

import java.util.Optional;

public class Game {
    Player player; // In reality you'd have a list here

    public Game() {
        System.out.println("Game starts");
        player = new PlayerOne();
    }

    public void play() {
        Message<Boolean> message1 = new Message<>("x", Boolean.class, true, false, true);
        Message<Integer> message2 = new Message<>("y", Integer.class, "Hello");
        Result result1 = sendMessage(player, message1);
        System.out.println("Response 1: " + result1.getResult());
        Result result2 = sendMessage(player, message2);
        System.out.println("Response 2: " + result2.getResult());
    }

    private Result sendMessage(Player player, Message<?> message1) {
        return Optional.ofNullable(player)
                .map(Game::createCommunicator)
                .map(comm -> comm.executeCommand(message1))
                .get();
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.play();
    }

    private static PlayerCommunicator createCommunicator(Player player) {
        if (player instanceof NativePlayer) {
            return new NativePlayerCommunicator((NativePlayer) player);
        }
        return new ExternalPlayerCommunicator((ExternalPlayer) player);
    }
}

public abstract class Player {}

public class ExternalPlayer extends Player {}

public abstract class NativePlayer extends Player {
    abstract boolean x(Boolean a, Boolean b, Boolean c);
    abstract Integer y(String yParam);
    abstract Void z(Void zParam);
}

public abstract class PlayerCommunicator {
    public abstract Result executeCommand(Message message);
}

import java.lang.reflect.Method;
public class NativePlayerCommunicator extends PlayerCommunicator {
    private NativePlayer player;
    public NativePlayerCommunicator(NativePlayer player) { this.player = player; }
    public Result executeCommand(Message message) {
        try {
            Method method = player.getClass().getDeclaredMethod(message.getMethod(), message.getParamTypes());
            return new Result(method.invoke(player, message.getArguments()));
        } catch (Exception e) { throw new RuntimeException(e); }
    }
}

public class ExternalPlayerCommunicator extends PlayerCommunicator {
    private ExternalPlayer player;
    public ExternalPlayerCommunicator(ExternalPlayer player) { this.player = player; }
    @Override
    public Result executeCommand(Message message) { /* Do some IO stuff */ return null; }
}

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Message<OUT> {
    private final String method;
    private final Class<OUT> returnType;
    private final Object[] arguments;

    public Message(final String method, final Class<OUT> returnType, final Object... arguments) {
        this.method = method;
        this.returnType = returnType;
        this.arguments = arguments;
    }

    public String getMethod() { return method; }
    public Class<OUT> getReturnType() { return returnType; }
    public Object[] getArguments() { return arguments; }

    public Class[] getParamTypes() {
        List<Class> classes = Arrays.stream(arguments).map(Object::getClass).collect(Collectors.toList());
        Class[] classArray = Arrays.copyOf(classes.toArray(), classes.size(), Class[].class);
        return classArray;
    }
}

public class PlayerOne extends NativePlayer {
    @Override
    boolean x(Boolean a, Boolean b, Boolean c) {
        System.out.println(String.format("x called: %b %b %b", a, b, c));
        return a || b || c;
    }

    @Override
    Integer y(String yParam) {
        System.out.println("y called: " + yParam);
        return yParam.length();
    }

    @Override
    Void z(Void zParam) {
        System.out.println("z called");
        return null;
    }
}

public class Result {
    private final Object result;
    public Result(Object result) { this.result = result; }
    public Object getResult() { return result; }
}

(PS। मेरे मन में अन्य कीवर्ड जो मैं अभी कुछ भी उपयोगी नहीं बता सकते हैं: कमांड पैटर्न , विज़िटर पैटर्न , java.lang.reflect.ParameterizedType )


मेरा लक्ष्य उस व्यक्ति की आवश्यकता को रोकना है जो Playerलेखन PlayerCommसे बिल्कुल बना है। जबकि कम्युनिकेटर इंटरफेस मेरे लिए ऑटोमैटिक कास्टिंग करता है, मैं अभी भी sendRequest()प्रत्येक विधि में समान फ़ंक्शन लिखने की समान समस्या में चलूँगा।
नाथन मेरिल

मैंने अपना उत्तर पुनः लिख दिया है। हालाँकि, अब मुझे एहसास हुआ है कि मुखौटा पैटर्न का उपयोग करना वास्तव में यहाँ जाने का तरीका हो सकता है, गैर-जावा प्रविष्टियों को एक जावा प्रविष्टि की तरह दिखता है। तो कुछ संचारकों या प्रतिबिंब के साथ कोई मूर्ख नहीं।
ज़ीरोएन

2
"ठीक है, तो इस तरह की बातें बढ़ गई और मैं निम्नलिखित दस वर्गों के साथ समाप्त हो गया" अगर मेरे पास एक निकेल होता ...
जैक

ओ, मेरी आँखें! वैसे भी हम इन 10 वर्गों के साथ जाने के लिए एक वर्ग आरेख प्राप्त कर सकते हैं? या क्या आप भी अपना मुखौटा पैटर्न उत्तर लिखने में व्यस्त हैं?
कैंडिड_ओरेंज

@CandiedOrange, तथ्य की बात के रूप में मुझे लगता है कि मैंने पहले ही इस प्रश्न के साथ पर्याप्त समय बिताया है। मैं उम्मीद कर रहा हूं कि कोई और समाधान का अपना संस्करण देगा, संभवतः एक मुखौटा पैटर्न का उपयोग करके।
ज़ीरोइन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.