mercredi 26 octobre 2016

How to better design a program with extended and specific methods being called

Its hard to phrase what I want in the title but here's what I'm trying to build:

I created a series of StaticAPI classes for every webservice I want to consume and they all implement the Storable interface which requires 2 methods: getInputStream() and getUniqueIdentifier(). So every StaticAPI class has a standard method to call its particular webservice but they also have each their own getInputStream(Params...) methods for the specific parameters each one could request:

interface Storable {
    InputStream getInputStream();
    String getUniqueIdentifier()
}

abstract class StaticAPI implements Storable {}

class TireAPI extends StaticAPI {
    InputStream getInputStream() {
        return getInputStream("16");
    }

    InputStream getInputStream(String diameter) {
        return HTTPRequest.request("tire/", diameter);
    }

    String getUniqueIdentifier() {
        return "tire_service_default";
    }

    String getUniqueIdentifier(String diameter) {
        return "tire_service_"+diameter;
    }
}

class WindShieldAPI extends StaticAPI {
    InputStream getInputStream() {
        return getInputStream("fume", "123");
    }

    InputStream getInputStream(String tone, String size) {
        return HTTPRequest.request("wind-shield/", String[] {tone, size});
    }

    String getUniqueIdentifier() {
        return "wind_shield_service_default";
    }

    String getUniqueIdentifier(String tone, String size) {
        return "wind_shield_service_"+tone+"_"+size;
    }
}

I then created a DataDriver interface that allows me to create several different methods of storing the data from these apis: FileSystemDataDriver, SQLDataDriver, CouchDBDataDriver and so on. The DataDriver interface expects a Storable object to be able to call the getInputStream() method but it also expects a uniqueIdentifier/inputStream to be able to store data coming from those specific methods with multiple parameters:

interface DataDriver {
    boolean exists(Storable api);
    boolean exists(String uniqueIdentifier);
    InputStream read(Storable api);
    InputStream read(String uniqueIdentifier);
    void save(Storable api)
    void save(String uniqueIdentifier, InputStream inputStream);
}

class FileSystemDataDriver implement DataDriver {

    Path directory;

    FileSystemDataDriver(Path directory) {
        this.directory = directory;
    }

    boolean exists(Storable api) {
        exists(api.getUniqueIdentifier());
    }

    boolean exists(String identifier) {
        return Files.exists(directory.resolve(identifier));
    }

    InputStream read(Storable api) {
        return read(api.getUniqueIdentifier());
    }

    InputStream read(String uniqueIdentifier) {
        return Files.newInputStream(directory.resolve(uniqueIdentifier));
    } 

    void save(Storable api) {
        save(api.getUniqueIdentifier(), api.getInputStream());
    }

    void save(String uniqueIdentifier, InputStream inputStream) {
        FileUtils.copyInputStreamToFile(inputStream, directory.resolve(uniqueIdentifier).toFile());
    }
}

So all of this code is just to explain what I dont think is looking too good in my code. I created another class to abstract the download and storage of all these apis. The idea is for the entire program to only call static methods from the DataStore class and this class will make sure that if I dont have the data stored and accessible through a DataDriver that it is downloaded and stored before it is returned:

class DataStore {

    DataDriver dataDriver;

    DataStore(DataDriver dataDriver) {
        this.dataDriver = dataDriver;
    }

    static InputStream getTire() {
        StaticAPI tireAPI = new TireAPI();
        if(!dataDriver.exists(tireAPI))
            dataDriver.save(tireAPI);
        return dataDriver.read(tireAPI);
    }

    static InputStream getTire(String diameter) {
        TireAPI tireAPI = new TireAPI();
        if(!dataDriver.exists(tireAPI.getUniqueIdentifier(diameter)))
            dataDriver.save(tireAPI.getUniqueIdentifier(diameter), tireAPI.getInputStream(diameter));
        return dataDriver.read(tireAPI.getUniqueIdentifier(diameter));
    }

    static InputStream getWindShield() {
        StaticAPI windShieldAPI = new WindShieldAPI();
        if(!dataDriver.exists(windShieldAPI))
            dataDriver.save(windShieldAPI);
        return dataDriver.read(windShieldAPI);
    }

    static InputStream getTire(String tone, String size) {
        WindShieldAPI windShieldAPI = new WindShieldAPI();
        if(!dataDriver.exists(windShieldAPI.getUniqueIdentifier(tone, size)))
            dataDriver.save(windShieldAPI.getUniqueIdentifier(tone, size), windShieldAPI.getInputStream(tone, size));
        return dataDriver.read(windShieldAPI.getUniqueIdentifier(tone, size));
    }
}

So what is really bothering me here is that I have to always use getUniqueIdentifier(Params...) and getInputStream(Params...) to store the data from an api that has different parameters and these methods are not part of any interface or anything so its not looking really OO.

Is there another design pattern I could follow here to be able to allow for a StaticAPI class to have specific parameters set when the getInputStream(Params...) method is called and still be able to save it with a DataDriver by only passing the Storable class as a parameter? Is there a better way of writing this program to achieve this feature of storing the data in multiple different ways? The only other option I see is for the user to specify the parameters in the constructor or in a setDiameter() kind of thing, but I'm not sure that looks good either.

Any suggestions?

Aucun commentaire:

Enregistrer un commentaire