OOP - Các vấn đề gặp phải khi bắt trước tu duy của bộ não

Lần trước tôi có trình bày một quan niệm lập trình của mình, đó là thiết kế chương trình sao cho mô phỏng lại chính xác nhất những gì chúng ta nhìn nhận về một vấn đề - OOP - Lập trình mô phỏng thế giới quan con người. Tuy vậy chúng ta sẽ nhiều lúc tự hỏi trong một vài bài toán đặc biệt, yêu cầu đặc biệt thì liệu chúng ta có còn có thể bắt chước được những gì chúng ta suy nghĩ, đưa vào trong lập trình được hay không. Ở bài này, tôi sẽ khảo sát một vài yêu cầu mở rộng, và so sánh chúng với việc chúng ta xử lý thông thường để nhận ra rằng trong đa số các trường hợp, bản năng của bộ não đã làm tốt hơn kĩ năng lập trình cơ bản của chúng ta.

Thêm yêu cầu sử lý với đa ngôn ngữ vào bài toán:

Giờ đây chúng ta muốn RedheadDuck phải được dịch thành vịt đầu đỏ ở ngôn ngữ tiếng Việt, fly phải thành bay... Theo tư duy của bộ não, khi chúng ta là mới học ngoại ngữ, chúng ta cần một quyển tử điển và translate từng từ một. chưa quan tâm đến cấu trúc ngữ pháp vội, biên dịch qua ngôn ngữ lập trình chúng ta sẽ có một chương trình sẽ như sau:

Có một lớp Translator, có một list các từ cần translate trong lớp dictionary, chúng ta cần một quyển tử điển mà.

Translator của chúng ta sẽ như sau:

class Translator{
    static Dictionary vietnameseDic = new VietnameseDictionary();
    static public String translateSentence(String sentence, LanguageMode mode){
        Dictionary dictionary = findDictionary(mode);
        List<String> output = new ArrayList<String>();
        String[] words = sentence.split(" ");
        for (int i = 0; i < words.length; i++) {
            output.add(dictionary.translateWord(words[i]));
        }
        return  org.apache.commons.lang.StringUtils.join(output.toArray(), " ");
    }

    static public Dictionary findDictionary(LanguageMode mode){
        return mode == LanguageMode.Vietnamese ? vietnameseDic : null;
    }
}

Code cho phần dictionary sẽ như sau:

abstract class Dictionary{
    Map<String, String> words = new HashMap<>();
    String translateWord(String word){
        return words.get(word);
    }

    public void put(String englishWord, String foreignWord){
        words.put(englishWord, foreignWord);
    }
}

class VietnameseDictionary extends Dictionary{
    public VietnameseDictionary() {
        initDictionary();
    }

    private void initDictionary() {
        this.put("Duck", "vit");
        this.put("this", "đây");
        this.put("is", "là");
        this.put("a", "một");
        this.put("Mallar Duck", "vịt tròi");
        this.put("flying", "đang bay");
        this.put("fly", "bay");
        this.put("Decoy Duck", "vịt trang trí");
        this.put("Decoy", "trang trí");
        this.put("can't", "không thể");
    }
}

Chúng ta xây dựng class Output, chuyên quản lý việc phát ngôn như sau:

class Output{
    static LanguageMode mode = LanguageMode.English;
    static public void println(String value) {
        if(isOriginal()){
            System.out.println(value);
        }
        else{
            System.out.println(Translator.translateSentence(value, mode));
        }
    }

    static boolean isOriginal(){
        return mode == LanguageMode.English;
    }

    static public void setLanguageMode(LanguageMode newMode){
        mode = newMode;
    }
}

Thử xem kết quả chạy như thế nào:

public class Simular {
    public static void main(String[] args) {
        Duck mallar = new MallarDuck();
        mallar.introduce();
        mallar.perform("fly");

        Output.println("");
        Output.println("Dùng tiếng việt: ");

        Output.setLanguageMode(LanguageMode.Vietnamese);
        Output.println("");

        Duck decoy = new DecoyDuck();
        decoy.introduce();
        decoy.perform("fly");
    }
}

Ta có:

this is a Mallar Duck
flying

Dùng tiếng việt:

đây là một trang trí vit
trang trí vit không thể bay

Kết quả tạm chấp nhận được, mặc dù chúng ta mong đợi nó phải dịch ra là vịt trang trí chứ không phải trang trí vịt. -> Tiếp tục mở rộng ra với các yêu cầu cho bài toán ngôn ngữ như việc có các từ đồng âm, chữ viết hoa, v.v..., mặc dù việc implement chúng không phải là đơn giản nhưng với chiến lược code như thế này thì việc có thể áp dụng những yêu cầu như vậy vào lập trình cũng không phải là một vấn đề quá khó và việc thay đổi source code chắc chắn vẫn không làm ảnh hưởng đến chương trình chính.

Các yếu tố có thể thay đổi và dữ liệu cần lưu trữ lại:

Giờ đây chúng ta quan tâm đến trọng lượng của con vịt, muốn biết con vịt này nặng bao nhiêu kg. Đơn giản, đọc đến đây thì chúng ta chỉ cần add thêm đơn vị trọng lượng weight vào cho vịt là được.

class Duck {
    public Duck(String name) {
        this.name = name;
    }

    String name;
    List<Action> actions;
    Double weight;

    public void perform(String action) {
        String result =
            actions.stream().filter(s -> s.getName().equals(action)).map(s -> s.perform()).findAny().orElse(name + " can't " + action);
        Output.println(result);
    }

    public void introduce() {
        String result = "this is a " + name;
        Output.println(result);
    }

    public double getWeight() throws UnknownValueException {
        if (weight != null) {
            return weight;
        } else {
            throw new UnknownValueException();
        }
    }
}

class UnknownValueException extends Exception {
}

OK, vậy con vịt này giờ là 5kg, nhưng tháng trước nó nặng nhiêu cân, tháng sau thì có thể nặng bao nhiêu.