0

我有多个类,其属性需要写入文本文件。由于每个类都有不同的属性,因此每个类都需要不同的算法来编写。我正在尝试为此使用策略模式,但它似乎没有奏效 - 不知道这是否是正确的使用模式?

class A 
{
    void one;
    void two;
    void three;
}

class B
{
    void four;
    void five;
    void six;
    void seven;
}

class C
{
    void eight;
    void nine;
}

这就是我的设计遇到问题的地方,我如何将对象传递给具体的策略?

class DataParser
{
    Object object;

    void DataParser(Object object)
    {
        this.object = object;

        parsers.put(new ClassA(), new ClassAParser());
        parsers.put(new ClassB(), new ClassBParser());
        parsers.put(new ClassC(), new ClassCParser());
    }

    void writeData()
    {
        ParserInterface parser = parsers.get(this.object);
        /*
         * classAParser.setClassA(object);
         * classBParser.setClassB(object);
         * classCParser.setClassC(object):
        */
        parser.write();
    }
}

.

interface ParserInterface
{
    void write();
    void read();
}

.

class ClassAParser()
{
    ClassA classA;

    void setClassA(ClassA classA)
    {
        this.classA = classA;
    }

    void write()
    {
        PrinterWriter writer = new PrintWriter("ClassA.txt");

        writer.printLn(this.classA.getOne() + "|" + this.classA.getTwo() + "|" + this.classA.getThree());

        writer.close();
    }

    void read()
    {
    }
}

.

class ClassBParser()
{
    ClassB classB;

    void setClassB (ClassB classB )
    {
        this.classB = classB ;
    }

    void write()
    {
        PrinterWriter writer = new PrintWriter("ClassB.txt");

        writer.printLn(this.classB.getFour() + "|" + this.classB.getFive() + "|" + this.classB.getSix() + "|" + this.classB.getSeven());

        writer.close();
    }

    void read()
    {
    }
}

那么我可以简单地做这样的事情:

class Test()
{
    void testClassA()
    {
        ClassA classA = new ClassA();
        classA.setOne("One");
        classA.setTwo("Two");
        classA.setThree("Three");

        DataParser parser = new DataParser(classA);
        parser.writeData();
    }
}

那么 ClassA.txt 应该有以下内容:“one|two|three”

4

5 回答 5

1

这是一个长镜头,我在您的代码中看到以下内容:

  parsers.put(new ClassA(), new ClassAParser());

但我找不到你在哪里声明这个变量(我猜是错误的复制粘贴)无论如何,我假设你使用 HashMap 因为 put() 方法。如果是这种情况,您需要在 A、B、C 类中同时实现 equals() 和 hashCode()。请参阅此处为什么

了解 HashMap 中 equals 和 hashCode 的工作原理

http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html

(简而言之,如果您不覆盖这些方法,那么您传入的对象

       parsers.get(this.object);

应该与您放入地图中的对象之一完全相同,但在您的情况下不是)

于 2013-09-20T09:28:14.133 回答
1

我认为策略界面对于您想要实现的目标可能有点矫枉过正。一个界面可能会得到你想要的:

public interface Writable {
  void writeTo(PrintWriter writer);
}

class A implements Writable {
  String one;
  String two;
  String three;

  public void writeTo(PrintWriter writer) {
    // do the writing here
  }
}

重复ClassBClassC...

于 2013-09-20T09:23:41.350 回答
1

您可以为解析器使用通用接口。

public interface ParserInterface<T> {
    void setObject(T object);
    void read();
    void write();
}

public class ClassAParser implements ParserInterface<ClassA> { ... }

正如 Pitelk 提到的,对象和解析器的映射似乎是错误的。相反,您需要使用类映射来解析:

parsers.add(ClassA.class, new ClassAParser());
// etc.

此外:在构造函数中创建所有解析器实现的实例DataParser是不必要的开销。您可以使用if/else if链和Object.getClassinstanceof在构造函数中仅创建所需的实例,或者使映射成为类的静态成员。

writeData然后变成:

void <T> writeData()
{
    ParserInterface<T> parser = (ParserInterface<T>) parsers.get(this.object.getClass());
    parser.setObject((T) this.object);  // <-- same method for all of supported types
    parser.write();
}

编译器将生成有关未经检查的强制转换的警告。但是如果使用正确,即parsers.get(c)返回一个兼容的解析器,它可以被忽略或抑制。

于 2013-09-20T09:40:52.963 回答
1

如果您不想让您的类实现接口,则可以使用与Java: If-else instanceof extended classes中描述的相同模式。对于工厂类,您必须传递要写入的对象和写入位置。
另一种方法是通过这种方式使用模板方法模式:

abstract class ParserReaderWriter implements ParserInterface {
  protected abstract String[] getDataToWrite();
  protected abstract PrintWriter createWriter();
  void write() {
    Writer writer = createWriter();

    writer.println(StringUtils.join(getDataToWrite(),"|");
    writer.close();
  }
}

然后为所有作家创建一个作家

class AParserReaderWriter extends ParserReaderWriter {
  ClassA object;
  AParserReaderWriter(ClassA object) {
    this.object = object;
  }
  protected String[] getDataToWrite() {
    return new String[]{this.object.getOne(),...};
  }
  protected PrintWriter createWriter() {
    return new PrintWriter("a.txt");
  }
}
于 2013-09-20T09:41:50.097 回答
1

我认为这里不需要“策略”(至少在这种情况下这对我来说听起来太重了)。另外,我不会在这里明确地“映射”任何东西。

所以基本上我已经明白,您的应用程序中的某个时间会有给定类的对象,然后想要以您自己自由定义的格式创建文本文件。作为一项要求,这是完全有效的,因此我不会在这里向您指出任何约定或工具。但是我也明白,您不想在每个类中单独进行“序列化”,而是使用一个(自定义)“序列化程序”,可能是应用程序范围的。这是我的建议与其他答案不同的地方。

实际创建文本文件的方法至少需要这些信息:

  1. 实际包含属性值的对象
  2. 有哪些属性(甚至:实际上要考虑哪些属性)
  3. 要写入的文件的(基本)名称 - 以及要使用的字符编码,或者更一般地说,一个 Writer,或者适合您在这方面的特定要求的任何内容。

因此,我个人的方法是实现一个 Util 方法,该方法在您的情况允许的范围内尽可能具体,并且根据需要尽可能通用以避免重复代码。

在该方法中,我将迭代(使用反射):

  • 所有可访问(甚至所有声明的)字段
  • 所有带注释的字段

对于后一种变体,您需要实现自己的注释来标记所需的属性,或者只使用现有的“@Transient”注释来整理不需要的属性。等等,您肯定希望注释具有RetentionPolicy.RUNTIME

//...
@Retention( RetentionPolicy.RUNTIME )
public @interface MyAnnotation
//...

但也许您甚至不需要显式标记或选择属性,特别是如果您的类纯粹是保值的。

在建议的循环中访问给定属性后,只需使用 String.valueOf (ex-或隐式)将该属性的“内容”发送给 writer,或直接附加到文件。

Java 序列化通常旨在进一步降低对象“树”,因为您的任何属性都可能是它自己的复杂对象,需要再次或多或少复杂的序列化。

但我知道您在这里需要一个简单的“平面”解决方案。

于 2013-09-20T09:47:38.443 回答