EdSwArchitect / simple-injection

1 stars 0 forks source link

Attempting to understand DI #2

Open anthonychavis opened 1 month ago

anthonychavis commented 1 month 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 1 month ago

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

anthonychavis commented 1 month ago

Java classes don't need destructors?

anthonychavis commented 1 month 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 1 month ago

update: still thinking through it!

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

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

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

    @Override
    public String language() {
        return "English";
    }
}

// other service/worker/dependency
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";
    }

    @Override
    public String language() {
        return "Jamaican Patois";
    }
}

// injection class - knows it will speak, but doesn't care which language - shows how instantiation will occur - aka dependent class b/c provided another class to instantiate
public class SimpleIngection {
    private Speak language;

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

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

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

    public String curLang() {
        return language.language();
    }

    // private field initialized w/the dependency 
    // getters used to access dependency's public methods/fields; no getter = no access
}

// instantiate & use - demonstrates the DI - doesn't know how the service is created, but can use it
public class Main {
    public static void main(String[] args) {
        // SpeakEnglish service/worker/dependency instantiated & injected; assigned to var
        SimpleIngection english = new SimpleIngection(new SpeakEnglish());

        // SpeakCreole service/worker/dependency instantiated & injected; assigned to var
        SimpleIngection creole = new SimpleIngection(new SpeakCreole());

        // use
        System.out.println("A greeting like " + english.salutation() + " in " + english.curLang() + " is " + creole.salutation() + " in " + creole.curLang() + ".");
    }
}
// rough example of translator/language-learn app
EdSwArchitect commented 1 month 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 month 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 month 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.

anthonychavis commented 1 month ago

Thank you!

If this is right, think I got it! (still thinking through it a bit though) Not sure how to word this better yet:

anthonychavis commented 3 weeks ago

[note: just realized the smiling emoji is actually a laughing emoji]

My today's understanding: DI via interfaces boils down to simply being a way to generically type an object that a class receives.

DI in general offers:

anthonychavis commented 1 week ago

Still in the kitchen tryna jazz it up, so line nums might change, but this is what I've been cooking: https://github.com/anthonychavis/my-arduino-repo/blob/main/Theatre/Rabbit/Rabbit_Class/RabbitClass_Res.hpp

class start - line 48 dependency passed-by-ref - line 49 private constructor - line 134 static factory - line 150

was wondering, since I'm using a 3rd party library, is it best practive to enter that 3rd party class (Servo) into its own dependent class then use that dependent class as a dependency for Rabbit? That way it wouldn't go into the class where it'll be used straight from the source. Was also thinking that if it would be best practice, it would be a good situation to utilize an interface, right? thinking because with the interface i could be like, 'hey, i'm bringing this class in, but anyone who uses it is restricted to only use these methods.' like how you did in your example. ... think i just answered my own question

the program is very simple & functions as intended with far less lines of code, but want to practice. planning to organize the program into more files. definitely think the Rabbit class has grown too large. planning to split it.