gpt4 book ai didi

Java - 关于一些代码的观察者模式。模仿同步文本更新

转载 作者:行者123 更新时间:2023-11-30 09:23:29 26 4
gpt4 key购买 nike

我的代码的目的是模仿 Google 文档的功能 - 当用户在一台机器上键入时,他们键入的字母会出现在另一台机器上。出于简单的目的,每台机器在图形用户界面中键入文本,主类处理所有更改。

每台机器都有一个“编辑器”,并链接到总共 1 个“文件内容主题”。 “文件内容主题”应该进行用户所做的更改并将更新后的代码发送给所有“编辑者”

enter image description here

这从一个简单的驱动程序开始,我在其中创建一个文件内容主题,创建两个编辑器并将它们连接在一起

public class Driver {
public static void main(String[] args) {
FileContentSubject filecontentsubject = new FileContentSubject();

Editor e1 = new Editor(filecontentsubject);
Editor e2 = new Editor(filecontentsubject);

filecontentsubject.attach(e1);
filecontentsubject.attach(e2);
}
}

Editor 看起来像这样(两个弹出窗口,不是其余的): enter image description here

制作编辑器的代码在这里:

import javax.swing.*;
import java.util.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;

import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;


public class Editor extends JFrame implements DocumentListener, Observer {

private FileContentSubject reference;
private Document doc;

private JScrollPane textAreaScrollPane;
private JTextArea textArea;


public Editor(FileContentSubject filecontentsubject) {
super("Editor");
initComponents();

this.reference = filecontentsubject;
textArea.getDocument().addDocumentListener(reference);

}


private void initComponents(){

textArea = new JTextArea();
textArea.setColumns(5);
textArea.setLineWrap(true);
textArea.setRows(50);
textArea.setWrapStyleWord(true);

textAreaScrollPane = new JScrollPane(textArea);


setLocation(600,100);
setSize(500,400);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

getContentPane().setLayout(new BorderLayout());
getContentPane().add(textArea, BorderLayout.CENTER);

}


@Override
public void changedUpdate(DocumentEvent arg0) {
}


@Override
public void insertUpdate(DocumentEvent arg0) {
reference.insertUpdate(arg0);
}


@Override
public void removeUpdate(DocumentEvent arg0) {
reference.removeUpdate(arg0);
}


@Override
public void update() {
//textArea.setText(reference.getJTextArea());
//textArea.setText(reference.temp);

}
}

我的代码目前唯一的问题文件内容主题,当我尝试更改通过编辑器传递的代码时。我收到很多“无法在通知内突变”和“空指针异常”错误。生成此代码的代码如下,是我已注释掉的部分。

import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Position;


public class FileContentSubject implements Subject, DocumentListener {

private JTextArea textArea;
private Document doc;

private SubjectImpl reference;


@Override
public void attach(Observer o) {
reference.attach(o);
}

@Override
public void detach(Observer o) {
reference.detach(o);
}

@Override
public void notifyAllObservers() {
reference.notifyAllObservers();
}


public FileContentSubject(){
reference = new SubjectImpl();

textArea = new JTextArea();
textArea.setTabSize(5);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.getDocument().addDocumentListener(this);
}

@Override
public void changedUpdate(DocumentEvent arg0) {}

@Override
public void insertUpdate(DocumentEvent arg0) {
doc = (Document)arg0.getDocument();


// try {
// //this.textArea.setText(doc.getText(0, doc.getLength()-1));
// } catch (BadLocationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
notifyAllObservers();
}

@Override
public void removeUpdate(DocumentEvent arg0) {
doc = (Document)arg0.getDocument();

// try {
// this.textArea.setText(doc.getText(0, doc.getLength()-1));
// } catch (BadLocationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }

notifyAllObservers();
}





public String getJTextArea(){
return textArea.getText();
}
}

所以我的问题是,如何将文本传入(通过编辑器传入 DocumentEvent)到文件内容主题,让它更改文件内容主题,并通知所有编辑者?


让这一切发生的我的其他类(class)(不重要,但为清楚起见显示):

主题界面

/**
* Interface
*/
public interface Subject {

public void attach(Observer o);
public void detach(Observer o);

public void notifyAllObservers();

}

观察者界面

public interface Observer {

public void update();
}

SubjectImpl 类

import java.util.ArrayList;
import java.util.List;


public class SubjectImpl implements Subject {

private List <Observer> observers;

public SubjectImpl(){
observers = new ArrayList<Observer>();
}


@Override
public void attach(Observer o) {
observers.add(o);
}

@Override
public void detach(Observer o) {
observers.remove(o);
}

@Override
public void notifyAllObservers() {
for(Observer o: observers){
o.update();
}
}
}



回答

需要防止正在编辑自身的编辑器被更新。这是通过文档属性完成的,并将所有内容都保存在一个字符串中而不是 JTextArea 中。非常感谢 acdcjunior 的帮助,代码答案位于所选答案的最后三个代码块上。

最佳答案

java.lang.IllegalStateException: Attempt to mutate in notification 被抛出是因为在 FileContentSubject 的这些行中:

@Override
public void insertUpdate(DocumentEvent arg0) {
doc = (Document) arg0.getDocument();

try {
this.textArea.setText(doc.getText(0, doc.getLength()-1));
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
notifyAllObservers();
}

@Override
public void removeUpdate(DocumentEvent arg0) {
doc = (Document) arg0.getDocument();

try {
this.textArea.setText(doc.getText(0, doc.getLength()-1));
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

notifyAllObservers();
}

FileContentSubject 的构造函数中的这一行:

textArea.getDocument().addDocumentListener(this);

表明您正在尝试从 DocumentListener 方法内部更改 textArea 的值。

正如指出的那样here , 您应该为此目的使用 DocumentFilter

这是 DocumentFilter 使用的一个简单工作示例(它将所有键入的字符替换为其大写版本):

//UpcaseFilter.java
//A simple DocumentFilter that maps lowercase letters to uppercase.

import javax.swing.*;
import javax.swing.text.*;

public class UpcaseFilter extends DocumentFilter {

public void insertString(DocumentFilter.FilterBypass fb, int offset,
String text, AttributeSet attr) throws BadLocationException {
fb.insertString(offset, text.toUpperCase(), attr);
}

// no need to override remove(): inherited version allows all removals
public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
String text, AttributeSet attr) throws BadLocationException {
fb.replace(offset, length, text.toUpperCase(), attr);
}

public static void main(String[] args) {
DocumentFilter dfilter = new UpcaseFilter();

JTextArea jta = new JTextArea();
JTextField jtf = new JTextField();
((AbstractDocument) jta.getDocument()).setDocumentFilter(dfilter);
((AbstractDocument) jtf.getDocument()).setDocumentFilter(dfilter);

JFrame frame = new JFrame("UpcaseFilter");
frame.getContentPane().add(jta, java.awt.BorderLayout.CENTER);
frame.getContentPane().add(jtf, java.awt.BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 120);
frame.setVisible(true);
}
}

(以上例子取自Java Swing, 2nd Edition书,chapter 22。)

这就是引发异常的原因,但您必须做更多的工作来更正您的代码:

简而言之,问题在于您的代码中发生了这种情况:

  • Editor 实例(我们称它为 edt)textArea 发生变化时...
  • ...FileContentSubject(因为它是一个 DocumentListener)注意到该事件,然后通知它所有注册的观察者(包括 edt! ) 调用他们的 update() 方法...
  • ... edtupdate() 方法改变了它的 textArea 并且...瞧!您正在尝试更改启动事件的 textArea(在事件结束之前)!

然后您所要做的就是找到一种不通知事件发起人的方法。下面的代码为此使用了 Document.putProperty()Document.getProperty():它分离事件的源代码编辑器 (reference.detach(e);), 将更改通知所有人,并重新附加它 (reference.attach(e);)。

(此外,我用 String 替换了 FileContentSubjectJTextArea,因为 String 就足够了.)

因此,这是更改后的 FileContentSubject 代码:

import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

public class FileContentSubject implements Subject, DocumentListener {

// private JTextArea textArea; // removed as a String field will suffice
// private Document doc; // should not be a field!

private String state;

public String getState() {
return this.state;
}

private SubjectImpl reference;

@Override
public void attach(Observer o) {
reference.attach(o);
}

@Override
public void detach(Observer o) {
reference.detach(o);
}

@Override
public void notifyAllObservers() {
reference.notifyAllObservers();
}

public FileContentSubject() {
reference = new SubjectImpl();

// textArea = new JTextArea();
// textArea.setTabSize(5);
// textArea.setLineWrap(true);
// textArea.setWrapStyleWord(true);
// textArea.getDocument().addDocumentListener(this);
}

@Override
public void changedUpdate(DocumentEvent arg0) {
}

@Override
public void insertUpdate(DocumentEvent arg0) {
Document doc = (Document) arg0.getDocument();
try {
// this.textArea.setText(doc.getText(0, doc.getLength()-1));
this.state = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Editor e = (Editor) doc.getProperty("ownerEditor");
reference.detach(e); // so it will not be notified of its own change
notifyAllObservers(); // tell everybody else to catch up with the changes
reference.attach(e); // reattaches the editor
}

@Override
public void removeUpdate(DocumentEvent arg0) {
Document doc = (Document) arg0.getDocument();
try {
// this.textArea.setText(doc.getText(0, doc.getLength()-1));
this.state = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Editor e = (Editor) doc.getProperty("ownerEditor");
reference.detach(e); // so it will not be notified of its own change
notifyAllObservers(); // tell everybody else to catch up with the changes
reference.attach(e); // reattaches the editor
}

// public String getJTextArea() {
// return textArea.getText();
// }
}

更改后的 Editor 的构造函数:

public Editor(FileContentSubject filecontentsubject) {
super("Editor");
initComponents();

this.reference = filecontentsubject;
textArea.getDocument().addDocumentListener(reference);
textArea.getDocument().putProperty("ownerEditor", this); // <---- ADDED LINE
}

update():

@Override
public void update() {
//textArea.setText(reference.getJTextArea());
//textArea.setText(reference.temp);
textArea.setText(reference.getState()); // ADDED
}

就是这样!

关于Java - 关于一些代码的观察者模式。模仿同步文本更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16004086/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com