简体   繁体   English

无法处理策略模式并将多个类写入文本文件

[英]Having trouble with strategy pattern and writing multiple classes to text file

I have multiple classes whose properties need to be written to a text file. 我有多个需要将其属性写入文本文件的类。 Since each class has different properties each requires a different algorithm to write. 由于每个类具有不同的属性,因此每个类都需要使用不同的算法来编写。 I'm trying to use a strategy pattern for this but it doesn't seem to be working out - don't know if this is even the correct pattern to use? 我正在尝试为此使用策略模式,但似乎无法解决问题-不知道这是否是正确的模式?

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

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

class C
{
    void eight;
    void nine;
}

This is where im having trouble with my design, how would I pass the object into the concrete strategy? 这是我在设计上遇到麻烦的地方,如何将对象传递给具体策略?

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()
    {
    }
}

So then I can just simply do something like this: 因此,我可以简单地执行以下操作:

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

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

Then the ClassA.txt should have the following: "one|two|three" 然后,ClassA.txt应具有以下内容:“一个|两个|三个”

I think the strategy interface might be a little overkill for what you are trying to achieve. 我认为策略界面可能对您要实现的目标有些过大。 An interface will probably get you what you want: 一个接口可能会为您提供所需的东西:

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

Repeat for ClassB and ClassC ... ClassBClassC重复ClassC ...

Here is a long shot , i have seen in your code the following: 这是一个远景,我在您的代码中看到以下内容:

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

but i cannot find where you declare this variable (i guess is wrong copy-paste) Anyway, i assume that you use a HashMap because of the method put(). 但我找不到您在何处声明此变量(我猜错了复制粘贴),无论如何,由于方法put(),我认为您使用的是HashMap。 If this is the case you need to implement both equals() and hashCode() in the classes A, B, C. see here why 如果是这种情况,则需要在类A,B,C中同时实现equals()和hashCode()。

Understanding the workings of equals and hashCode in a HashMap 了解HashMap中的equals和hashCode的工作原理

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

(in short words if you don't override these methods then the object you pass in the (简而言之,如果您不覆盖这些方法,则您在

       parsers.get(this.object);

should be the exact same instance with the one of the objects you have putted in your map but in your case it is not) 应该与您放置在地图中的对象之一完全相同,但是在您的情况下不是)

You could use a generic interface for the parser. 您可以为解析器使用通用接口。

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

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

As Pitelk mentioned, the map of object and parser seems wrong. 如Pitelk所述,对象和解析器的映射似乎是错误的。 Instead you'll want to use a map of class to parser: 相反,您将需要使用类映射来解析器:

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

Besides: creating instances of all parser implementations in the constructor of DataParser is unnecessary overhead. 此外:在DataParser的构造函数中创建所有解析器实现的DataParser是不必要的开销。 You could create only the needed instance using an if / else if chain and Object.getClass or instanceof in the constructor or make the map a static member of your class. 您可以使用if / else if链和Object.getClassinstanceof在构造函数中仅创建所需的实例,或者使映射成为类的静态成员。

writeData then becomes: 然后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();
}

The compiler will generate a warning about unchecked casts. 编译器将生成有关未经检查的强制转换的警告。 But if used correctly, ie parsers.get(c) returns a compatible parser, it can be ignored or suppressed. 但是,如果使用正确,即parsers.get(c)返回兼容的解析器,则可以将其忽略或抑制。

You can use the same mode as described in Java: If-else instanceof extended classes if you don't want to let your class implements an interface. 您可以使用Java中描述的相同模式:如果不想让您的类实现接口,则为扩展类的 if -else instance To factory class you have to pass object to write and where to write. 对于工厂类,您必须传递要写入的对象以及在何处写入。
Another way can be to use a template method pattern in this way: 另一种方法可以是通过这种方式使用模板方法模式:

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

then create a writer for all writer 然后为所有作家创建作家

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

I don't see the need for a "strategy", here (at least that sounds too heavy-weight for me in this case). 我认为这里不需要“战略”(至少在这种情况下,这听起来对我来说太沉重了)。 Also, I wouldn't "map" anything explicitly here. 另外,我不会在这里明确“映射”任何内容。

So basically I've understood that you'll have objects of the given classes at some time in your application, and then want to create text files in a format defined freely by yourself. 因此,基本上我已经了解到,您的应用程序中有时会拥有给定类的对象,然后希望以自己自由定义的格式创建文本文件。 This is perfectly valid as a requirement, so I won't point you to any conventions or tools, here. 这是完全有效的要求,因此在这里我不会为您指出任何约定或工具。 However I also understand that you don't want to do the "serialization" individually within each of the classes, but rather use one (custom) "serializer", probably application-wide. 但是,我也了解到,您不想在每个类中单独进行“序列化”,而是使用一个(自定义)“序列化器”,可能在整个应用程序范围内。 This is where my suggestion differs from other answers. 这是我的建议与其他答案不同的地方。

The method which will actually create the text files needs at least these pieces of information: 实际创建文本文件的方法至少需要以下信息:

  1. the object(s) actually containing the property values 实际包含属性值的对象
  2. what properties there are (or even: which ones are actually to be considered) 有哪些属性(甚至:实际要考虑哪些属性)
  3. the (base) name of the file to write to - and the character encoding to use, or, more generally, a Writer, or whatever fits your specific requirements on this aspect. 要写入的文件的(基本)名称-使用的字符编码,或更一般而言,使用Writer或符合您对此方面的特定要求的任何字符。

My personal approch would thus be to implement a Util method being as specific as allowed in your case, and as generic as needed to avoid duplicate code. 因此,我个人的做法是实现一个Util方法,该方法应根据您的情况允许,并且应根据需要采用通用方法,以避免重复代码。

Within that method, I'd iterate (using reflection) over either: 在该方法中,我将迭代(使用反射):

  • all accessible (or even all declared) fields 所有可访问的(甚至所有已声明的)字段
  • all annotated fields 所有带注释的字段

For the latter variant you'll need to implement your own Annotation to mark the desired properties, or just use the existing "@Transient" annotation to sort out the non-wanted ones. 对于后一种变体,您需要实现自己的注释来标记所需的属性,或者仅使用现有的“ @Transient”注释来整理不需要的注释。 Wait, you'll certainly want the annotation to have RetentionPolicy.RUNTIME : 等一下,您肯定会希望注释具有RetentionPolicy.RUNTIME

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

But maybe you don't even need to explicitly mark or select properties, particularly if your classes are purely value-holding. 但是也许您甚至不需要显式标记或选择属性,尤其是在您的类纯粹是价值持有的情况下。

Once you've accessed a given property within the suggested loop, simply make use of String.valueOf (ex- or implicitly) to send the "contents" of that property to a writer, or append to a file directly. 在建议的循环中访问了给定的属性后,只需使用String.valueOf(外部或隐式)将该属性的“内容”发送给编写者,或直接附加到文件。

Java serialization generally aims to descend further in object "trees", since any of your properties may be a complex object of its own, requiring more or less sophisticated serialization again. Java序列化通常旨在进一步深入对象“树”,因为您的任何属性都可能是其自身的复杂对象,因此或多或少需要复杂的序列化。

But I've understood that you rather need a simple, "flat" solution here. 但是我了解到,您这里需要一个简单的“扁平”解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM