简体   繁体   中英

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"

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

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(). If this is the case you need to implement both equals() and hashCode() in the classes A, B, C. see here why

Understanding the workings of equals and hashCode in a HashMap

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

writeData then becomes:

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.

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

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.

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. Wait, you'll certainly want the annotation to have 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.

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.

But I've understood that you rather need a simple, "flat" solution here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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