EdSwArchitect / simple-injection

0 stars 0 forks source link

Attempting to understand DI #2

Open anthonychavis opened 2 days ago

anthonychavis commented 2 days ago

recap

  1. Define an interface that has a method named sayHi() that returns a string
  2. Define an object named EnglishSayHi that implements [sayHi()] returning a string of "Hi"
  3. Define an object named Salutation that defines an attribute named sayIt.
  4. Have the constructor take a parameter of the interface type and assign it to the sayIt object. Have your main create it call it
  5. Update to have a setter that sets the attribute and the run it again

seeing the Java

class SI {
  Speak& language;  // have to check syntax

  public:
    SI() = default;

    // have to check syntax; think this puts it on the stack instead of heap
    SI(Speak& serviceWorker): language(serviceWorker) {}

    // probably need a destructor
    ~SI() {}

    // have to check syntax
    void set_language(Speak& serviceWorker) {
      serviceWorker language;
    }
};

(will try to fully build it out in cpp with correct syntax)

anthonychavis commented 2 days ago

!!! if used for 3rd party APIs (or i guess anything), private methods could be used to verify/sanitize before outputting data ?

anthonychavis commented 2 days ago

Java classes don't need destructors?

anthonychavis commented 2 days ago

~wait! just realized main() is static!!!~ ~I see how it's called! So, what I was saying about its convenience is legit!~

~you're not using main's param though~

SimpleIngection.main(<arr_of_args>);

~nope, java is very different.~

param typo = compile-time error? public static void main(String[] args) {};

anthonychavis commented 2 days ago

update: still thinking through it! want to ask about the difference between what i'm working on here and what you have.

// service interface - aka contract - aka dependency ?
public interface Speak {
    // contract methods
    public String sayHi();
    public Sting sayBye();
    public String language();
}

// service/worker - defines the methods for a specific implementation of the interface - one of the classes that can vary for testing/feature-expansion
public class SpeakEnglish implements Speak {
    // define methods req'd by contract
    @Override
    public String sayHi() {
        return "Hello";
    }

    @Override
    public String sayBye() {
        return "Goodbye";
    }

    // method never used ?
    @Override
    public String language() {
        return "English";
    }
}

// other service/worker
public class SpeakCreole implements Speak {
    // define methods req'd by contract
    @Override
    public String sayHi() {
        return "Wah gwaan";
    }

    @Override
    public String sayBye() {
        return "Walk gud";
    }

    // method never used ?
    @Override
    public String language() {
        return "Jamaican Patois";
    }
}

// injection class - inject via constructor - knows it will speak, but doesn't care which language - shows how instantiation will occur - aka dependent class ?
public class SimpleIngection {
    private Speak language;  // this is why this class is aka the dependent class

    // constructor injection
    public SimpleIngection(Speak language) {
        this.language = language;
    }

    // available methods after injection
    public String salutation() {
        return language.sayHi();
    }

    public String weOut() {
        return language.sayBye();
    }

    // use unused method ?
    public String curLang() {
        return language.language();
    }
    // ? allowed to access .language() without using it in the dependent/injection class?
    // after injection class instantiated, is it still possible to access methods that aren't used here? this isn't exactly class inheritance; so, i'd think they'd fall off - unless inheritance applied via the dependency/contract class ?
        // realized much later that was a dumb @$$ thought because the methods here are acting as getters since the service is initialized to a private field - cant access its contents w/o getters
}

// instantiate & use ? - doesn't know how the service is created, but can use it
public class Main {
    public static void main(String[] args) {
        // injection via constructor is more strict than via setter; so, overloading the constructor like i was thinking of doing in cpp doesn't make sense b/c we'd either want the strictness or the flexibility ?
        // english service instantiated
        SimpleIngection english = new SimpleIngection(new SpeakEnglish());

        // creole service instantiated
        SimpleIngection creole = new SimpleIngection(new SpeakCreole());

        // use
        System.out.println("A greeting like " + english.salutation() + " in " + english.curLang() + " is " + creole.salutation() + " in " + creole.curLang() + ".");
    }
}
// might be example of translator/language-learn app ?
EdSwArchitect commented 1 day ago
the implements key word is used to type a class to the Speak data type
it looks similar to inheritance with extends
so, a service/worker class is kind of a parallel term to child class?

Java has single inheritance. It can only directly subclass one class, unlike C++. Interfaces are a way around it. Only the developer that uses that Interface, must define the methods defined.

you added 2 methods
3 total contract method

The contract (Interface) methods:

the methods in Speak must be defined (because contract?) in classes implementing its type

Yes.

yesterday I was first thinking you meant it should be mutable; like the interface type could change as needed here

Injection objects are not mutable. Once set, they are set. Or at least, they should be. My implementation is simple and doesn't disable it.

so, this injection class can be a "permanent fixture" somewhere in the code & what gets spit out will change based on whatever service/worker is plopped in as an arg!

Yes.

so, simpleIngection.salutation(); or simpleIngection.weOut(); can be in multiple files/modules but the service/worker only has to be inserted to the injection class in one place, right?

Yes.

EdSwArchitect commented 1 day ago
so, when needing to change the data feed for some reason (guessing the database changed, using a test database, using a new database and didn't want to change the old database until making sure the new database worked correctly, or switching to a different 3rd party API, or something) it's easy swap out/in to see the changes across the entire codebase (or wherever the injection class is used), right ?

Exactly. In the real world, the developer using a package would set a configuration item to tell the software what class to use. The class will implement the method(s) and the software will inject it into it's code and do its thing. Database and cache is a good example.

EdSwArchitect commented 1 day ago
the difference in instantiating the injection class b/c SSI's constructor doesn't get an arg

The major injection frameworks expect empty constructors and then introspect the classes (or use annotations) to call setup , tear down, status, etc methods. If they don't use annotations, the code will introspect the class to see if it implements and interface and then calls the methods.

Java classes don't need destructors?

No. The JVM handles it.