Thiết kế mã: Phân quyền của các hàm tùy ý


9

Trên PPCG, chúng tôi thường xuyên có các thử thách King of the Hill , giúp các bot mã khác nhau chống lại nhau. Chúng tôi không muốn giới hạn những thách thức này trong một ngôn ngữ duy nhất, vì vậy chúng tôi thực hiện giao tiếp đa nền tảng qua I / O tiêu chuẩn.

Mục tiêu của tôi là viết một khuôn khổ mà các nhà văn thách thức sẽ có thể sử dụng để làm cho việc viết những thách thức này trở nên dễ dàng hơn. Tôi đã đưa ra các yêu cầu sau đây tôi muốn thực hiện:

  1. Người viết thử thách có thể tạo ra một lớp trong đó các phương thức đại diện cho mỗi giao tiếp riêng biệt . Ví dụ, trong thử thách Good vs Evil của chúng tôi , người viết sẽ tạo ra một Playerlớp có abstract boolean vote(List<List<Boolean>> history)phương thức trên đó.

  2. Bộ điều khiển có thể cung cấp các thể hiện của lớp trên giao tiếp thông qua I / O tiêu chuẩn khi các phương thức nói trên được gọi . Điều đó nói rằng, không phải tất cả các trường hợp của lớp trên sẽ nhất thiết phải giao tiếp qua I / O tiêu chuẩn. 3 trong số các bot có thể là các bot Java nguyên gốc (chỉ đơn giản là ghi đè Playerlớp, trong đó 2 khác ở ngôn ngữ khác)

  3. Các phương thức sẽ không luôn có cùng số lượng đối số (chúng cũng sẽ không luôn có giá trị trả về)

  4. Tôi muốn người viết thử thách phải làm ít việc nhất có thể để làm việc với khuôn khổ của tôi.

Tôi không chống lại việc sử dụng sự phản chiếu để giải quyết những vấn đề này. Tôi đã xem xét yêu cầu người viết thử thách phải làm một cái gì đó như:

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);
    }
}

nhưng nếu có một số phương pháp, điều này có thể trở nên khá lặp đi lặp lại và việc truyền liên tục không thú vị. ( sendMessagetrong ví dụ này sẽ chấp nhận một số lượng Objectđối số khác nhau và trả về một Object)

Có cách nào tốt hơn để làm điều này?


1
Tôi bối rối về " PlayerComm extends Player" -part. Có phải tất cả những người đăng ký Java đều mở rộng PlayerPlayerCommlớp này là một bộ điều hợp cho những người không tham gia Java?
ZeroOne

Vâng, đúng vậy
Nathan Merrill

Vì vậy, vì tò mò ... Bạn đã quản lý để đưa ra một loại giải pháp tốt đẹp cho việc này?
ZeroOne

Không. Tôi không nghĩ những gì tôi muốn là có thể có trong Java: /
Nathan Merrill

Câu trả lời:


1

OK, mọi thứ đã leo thang và tôi đã kết thúc với mười lớp sau ...

Điểm mấu chốt trong phương thức này là tất cả các giao tiếp xảy ra khi sử dụng Messagelớp, tức là trò chơi không bao giờ gọi trực tiếp các phương thức của người chơi mà luôn sử dụng lớp giao tiếp từ khung của bạn. Có một trình truyền thông dựa trên sự phản chiếu cho các lớp Java gốc và sau đó phải có một trình truyền thông tùy chỉnh cho tất cả các trình phát không phải Java. Message<Integer> message = new Message<>("say", Integer.class, "Hello");sẽ khởi tạo một thông điệp tới một phương thức có tên sayvới tham số "Hello"trả về một Integer. Điều này sau đó được chuyển đến một bộ truyền thông (được tạo bằng cách sử dụng một nhà máy dựa trên loại trình phát) sau đó thực thi lệnh.

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. Các từ khóa khác trong tâm trí tôi hiện tại tôi không thể tinh chỉnh thành bất kỳ thứ gì hữu ích: Mẫu lệnh , Mẫu khách truy cập , java.lang.reflect.ParameterizedType )


Mục tiêu của tôi là ngăn chặn yêu cầu người thực hiện Playerbằng văn bản PlayerComm. Trong khi các giao diện truyền thông thực hiện truyền tự động cho tôi, tôi vẫn gặp phải vấn đề tương tự là phải viết cùng một sendRequest()chức năng trong mỗi phương thức.
Nathan Merrill

Tôi đã viết lại câu trả lời của tôi. Tuy nhiên, bây giờ tôi nhận ra rằng sử dụng mẫu mặt tiền thực sự có thể là cách để đi đến đây, bằng cách gói các mục không phải Java vào những gì trông giống hệt như mục Java. Vì vậy, không có trò đùa với một số người giao tiếp hoặc phản ánh.
ZeroOne

2
"OK, mọi thứ đã leo thang và tôi đã kết thúc với mười lớp sau" Nếu tôi có niken ...
Jack

Ow, đôi mắt của tôi! Dù sao chúng ta có thể có một sơ đồ lớp để đi với 10 lớp này? Hoặc bạn quá bận rộn để viết câu trả lời mẫu mặt tiền của bạn?
candied_orange

@CandiedOrange, như một vấn đề thực tế, tôi nghĩ rằng tôi đã dành đủ thời gian cho câu hỏi này. Tôi hy vọng người khác sẽ đưa ra phiên bản giải pháp của họ, có thể sử dụng mô hình mặt tiền.
ZeroOne
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.