Java Observer 模式实现文本编辑器功能:统计单词数量、排序和频次

本文介绍使用 Java Observer 模式实现一个文本编辑器功能,包含统计单词数量、排序单词列表以及计算单词出现频率。用户可以在文本编辑区输入文本,观察者模式会实时更新统计信息,展示在三个独立的信息区域。

功能需求

该文本编辑软件需提供如下功能:

  1. 在文本编辑窗口中包含一个可编辑文本区和 3 个文本信息统计区。
  2. 用户可以在可编辑文本区对文本进行缩辑操作。
  3. 第一个文本信息统计区用于显示可编辑文本区中出现的单词总数量和字符总数量。
  4. 第二个文本信息统计区用于显示可编辑文本区中出现的单词(去重后按照字典序排序)。
  5. 第三个文本信息统计区用于按照出频次降序显示可编辑文本区中出现的单词以及每个单词出现的次数(例如 'hello', 5)。

设计思路

采用观察者模式设计该功能,具体实现如下:

  1. 主体类:WordCount 类负责存储文本内容,并计算单词数量和字符数量。它实现了 Subject 接口,可以维护观察者列表。
  2. 观察者类:
    • WordList 类负责将文本内容拆分成单词列表并进行排序,并实现 Observer 接口以接收更新通知。
    • WordFrequency 类负责统计每个单词出现的频率并按照降序排序,并实现 Observer 接口以接收更新通知。

代码实现

// Observer.java
public interface Observer {
    void update();
}

// Subject.java
import java.util.ArrayList;
import java.util.List;

public class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// WordCount.java
public class WordCount extends Subject {
    private String text;
    private int wordCount;
    private int charCount;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
        wordCount = countWords(text);
        charCount = countChars(text);
        notifyObservers();
    }

    public int getWordCount() {
        return wordCount;
    }

    public int getCharCount() {
        return charCount;
    }

    private int countWords(String text) {
        String[] words = text.split("\s+");
        return words.length;
    }

    private int countChars(String text) {
        return text.length();
    }
}

// WordList.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class WordList implements Observer {
    private WordCount wordCount;
    private List<String> words = new ArrayList<>();

    public WordList(WordCount wordCount) {
        this.wordCount = wordCount;
        wordCount.attach(this);
    }

    public List<String> getWords() {
        return words;
    }

    @Override
    public void update() {
        String text = wordCount.getText();
        String[] wordArray = text.split("\s+");
        words = Arrays.asList(wordArray);
        Collections.sort(words);
    }
}

// WordFrequency.java
import java.util.*;

public class WordFrequency implements Observer {
    private WordCount wordCount;
    private Map<String, Integer> frequencyMap = new HashMap<>();

    public WordFrequency(WordCount wordCount) {
        this.wordCount = wordCount;
        wordCount.attach(this);
    }

    public Map<String, Integer> getFrequencyMap() {
        return frequencyMap;
    }

    @Override
    public void update() {
        String text = wordCount.getText();
        String[] wordArray = text.split("\s+");
        List<String> words = Arrays.asList(wordArray);
        frequencyMap = new HashMap<>();
        for (String word : words) {
            if (frequencyMap.containsKey(word)) {
                frequencyMap.put(word, frequencyMap.get(word) + 1);
            } else {
                frequencyMap.put(word, 1);
            }
        }
        List<Map.Entry<String, Integer>> list = new ArrayList<>(frequencyMap.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        frequencyMap = new LinkedHashMap<>();
        for (Map.Entry<String, Integer> entry : list) {
            frequencyMap.put(entry.getKey(), entry.getValue());
        }
    }
}

// TextEditor.java
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TextEditor {
    private JFrame frame;
    private JTextArea textArea;
    private JLabel wordCountLabel;
    private JLabel charCountLabel;
    private JList<String> wordList;
    private JList<String> frequencyList;
    private WordCount wordCount;
    private WordList wordListObserver;
    private WordFrequency wordFrequencyObserver;

    public TextEditor() {
        frame = new JFrame("Text Editor");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        textArea = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(textArea);
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        JPanel infoPanel = new JPanel(new GridLayout(1, 2));
        wordCountLabel = new JLabel("Words: 0");
        wordCountLabel.setBorder(new TitledBorder("Word Count"));
        charCountLabel = new JLabel("Chars: 0");
        charCountLabel.setBorder(new TitledBorder("Char Count"));
        infoPanel.add(wordCountLabel);
        infoPanel.add(charCountLabel);
        frame.getContentPane().add(infoPanel, BorderLayout.SOUTH);

        JPanel wordPanel = new JPanel(new GridLayout(1, 2));
        wordList = new JList<>();
        wordList.setBorder(new TitledBorder("Words"));
        JScrollPane wordScrollPane = new JScrollPane(wordList);
        frequencyList = new JList<>();
        frequencyList.setBorder(new TitledBorder("Frequency"));
        JScrollPane frequencyScrollPane = new JScrollPane(frequencyList);
        wordPanel.add(wordScrollPane);
        wordPanel.add(frequencyScrollPane);
        frame.getContentPane().add(wordPanel, BorderLayout.EAST);

        wordCount = new WordCount();
        wordListObserver = new WordList(wordCount);
        wordFrequencyObserver = new WordFrequency(wordCount);

        textArea.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                updateCounts();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                updateCounts();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateCounts();
            }
        });

        frame.setSize(600, 400);
        frame.setVisible(true);
    }

    private void updateCounts() {
        wordCount.setText(textArea.getText());
        int wordCountValue = wordCount.getWordCount();
        int charCountValue = wordCount.getCharCount();
        wordCountLabel.setText(String.format("Words: %d", wordCountValue));
        charCountLabel.setText(String.format("Chars: %d", charCountValue));

        java.util.List<String> wordListValue = wordListObserver.getWords();
        java.util.List<String> frequencyListValue = new java.util.ArrayList<>();
        java.util.Map<String, Integer> frequencyMapValue = wordFrequencyObserver.getFrequencyMap();
        for (String word : wordListValue) {
            frequencyListValue.add(String.format("%s, %d", word, frequencyMapValue.get(word)));
        }
        wordList.setListData(wordListValue.toArray(new String[0]));
        frequencyList.setListData(frequencyListValue.toArray(new String[0]));
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TextEditor();
            }
        });
    }
}

运行结果

运行 TextEditor 类,即可看到文本编辑器界面,包含可编辑文本区和 3 个文本信息统计区。用户可以在可编辑文本区对文本进行缩辑操作,第一个文本信息统计区用于显示可编辑文本区中出现的单词总数量和字符总数量,第二个文本信息统计区用于显示可编辑文本区中出现的单词(去重后按照字典序排序),第三个文本信息统计区用于按照出频次降序显示可编辑文本区中出现的单词以及每个单词出现的次数。

总结

本文介绍了使用 Java Observer 模式实现文本编辑器功能的示例,展示了观察者模式在实时更新信息显示方面的应用。开发者可以根据自己的需求,扩展该功能,例如添加更多统计信息、自定义界面等。


原文地址: https://www.cveoy.top/t/topic/nz2m 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录