Generika a závislost inverze s více structs

0

Otázka

Snažím se vytvořit čisté architektury strukturu Rzi s některými structs pomocí vlastností pro závislost inverze.

Můj nápad je v podstatě mít:

  • A User model s jedno pole.
  • Úložiště, které je v souladu s úložiště vlastnost/rozhraní drží metoda pro načtení uživatelů z MySQL databáze.
  • Případ použití, které závisí na úložiště vlastnost/rozhraní, a obdrží instanci tohoto úložiště na instance s new metoda. Jsem také držitelem execute metoda pro spuštění úložiště akci.
  • Řadič, který závisí na případu užití znaku/rozhraní, a obdrží instanci tento případ použití při spouštění s new metoda. Je také držitelem execute metoda vyvolat případ užití akci.

  User:
  + id

  UserRepository complies with IUserRepository:
  - get_all_users: () -> Vec<Users>

  GetUsersUseCase complies with IGetUsersUseCase:
  + user_repository: IUserRepository
  - new: (user_repository: IUserRepository) -> GetUsersUseCase
  - execute: () -> Vec<Users>

  GetUsersController:
  + get_users_use_case: IGetUsersUseCase
  - new: (user_use_case: IGetUsersUseCase) -> GetUsersController
  - execute: () -> Vec<Users>

Mám provádění tohoto, ale mám problém s generics. Základní kód:

Hřiště: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ca1f4f9d6cfe4df2864d7e574966ad6b.

Kód:

// MODEL
#[derive(Debug)]
struct User {
  id: i8,
}

// REPOSITORY for DB
trait IUserRepository {
  fn get_users(&self) -> Vec<User>;
}

struct MySQLUserRepository;

impl IUserRepository for MySQLUserRepository {
  fn get_users(&self) -> Vec<User> {
    let mock_user1 = User { id: 1 };
    let mock_user2 = User { id: 2 };
    let mock_users = vec![mock_user1, mock_user2];

    mock_users
  }
}

// USE CASE
trait IGetUsersUseCase {
  fn new<T: IUserRepository>(repository: T) -> GetUsersUseCase<T>;
  fn execute(&self) -> Vec<User>;
}

struct GetUsersUseCase<T> {
  user_repo: T,
}

impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T> {
  fn new<K: IUserRepository>(user_repo: K) -> GetUsersUseCase<K> {
    GetUsersUseCase { user_repo }
  }

  fn execute(&self) -> Vec<User> {
    let users = self.user_repo.get_users();
    users
  }
}

// CONTROLLER for HTTP requests
struct GetUsersController<T> {
  get_users_use_case: T,
}

impl<T: IGetUsersUseCase> GetUsersController<T> {
  fn new(get_users_use_case: T) -> GetUsersController<T> {
    GetUsersController { get_users_use_case }
  }

  fn execute(&self) -> Vec<User> {
    let users = self.get_users_use_case.execute();
    users
  }
}

fn main() {
  // Lets imagine we are handling an HTTP request
  let mysql_repo = MySQLUserRepository {};
  // Error here: cannot infer type for type parameter `T` declared on the struct `GetUsersUseCase`
  let get_users_use_case = GetUsersUseCase::new(mysql_repo);
  let get_users_controller = GetUsersController::new(get_users_use_case);
  let users = get_users_controller.execute();
  println!("{:?}", users);
}

Jak můžete vidět, problém je v provedení pro GetUsersUseCase —impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T>—. Jako provádění obdrží dvě generické parametry, při stavbě GetUsersUseCase::new(mysql_repo) Já se zobrazit následující chyba:

cannot infer type for type parameter `T` declared on the struct `GetUsersUseCase`rustc(E0282)

Řešení by bylo, aby se user_repo na GetUsersUseCase veřejnost, a místo použití GetUsersUseCase::new(mysql_repo) instanci je jako obvykle:

[…]
struct GetUsersUseCase<T> {
  pub user_repo: T,
}
[…]
let get_users_use_case = GetUsersUseCase {
  user_repo: mysql_repo,
};
[…]

To funguje, ale já bych opravdu rád, aby zjistili, jak se postavit structs pomocí veřejné funkce, aniž by odhalil vlastní pole.

Zde je hřiště, takže toto pole veřejnost: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=917cee9d969dccd08c4e27753d04994f

Nějaký nápad bude vítán!

clean-architecture rust struct
2021-11-17 12:29:42
1

Nejlepší odpověď

1

Na rychlou opravu vašeho kódu:

// No more error here
let get_users_use_case = GetUsersUseCase::<MySQLUserRepository>::new(mysql_repo);

Proč tak?

To začíná od trait IGetUsersUseCase: nevyžaduje to obecný typ, nicméně, jeho metody fn new<T>() ano, tak jak struct GetUsersUseCase<T>. A podívejte se tady:

impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T> {
  fn new<K: IUserRepository>(user_repo: K) -> GetUsersUseCase<K> {
    GetUsersUseCase { user_repo }
  }
}

To je správné provádění mít takové vlastnosti, a to vlastně ukazuje, že T není uvedené Kje to jako, že jsou "jiné" (K je definován pro funkci a T je definován pro struct, obojí nejsou definovány pro znak). Proto, budete muset pomoci kompilátoru s turbofish operátor (GetUsersUseCase::<MySQLUserRepository>).

To není jediný způsob. Například, můžete přesunout generika od způsobu úroveň znaku úrovni.

// BEFORE
trait IGetUsersUseCase {
  fn new<T: IUserRepository>(repository: T) -> GetUsersUseCase<T>;
  fn execute(&self) -> Vec<User>;
}

// AFTER
trait IGetUsersUseCase<T: IUserRepository> {
  fn new(repository: T) -> GetUsersUseCase<T>;
  fn execute(&self) -> Vec<User>;
}

Nicméně, budete vidět, že věci se stávají poměrně složitá na další rys prohlášení. (Viz hřišti https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=38f41d8b50464acc28c7dc0c70842e6e).

Já bych doporučit , aby jste zkontrolovat svůj design "rozhraní" (vlastnosti). Nyní, máte jakési "dědictví" Repo -> UseCase -> Controller, nicméně, například, můžete provést správce jako soubor jednoduchých funkcí, a možná nepotřebujete generických typů všude.

2021-11-17 17:21:13

Díky moc, nejlepší odpověď někdy, je obtížné vyčíslit vaši pomoc. Snažil jsem se projít obecné na vlastnost úroveň místo metodu úrovni, ale nevěděl o tom PhantomData vynutit použití generického param.
miravelardo

O závislosti směru: jádro programu je model User. Ve vnější vrstvě máme GetUsersUseCase a IUserRepository, které na něm závisí. Ve vnější vrstvě, na jednom okraji, máme MySQLUserRepository v závislosti na IUserRepository a účetní jednotka User. Také ve vnější vrstvě, na druhém okraji, máme GetUsersController, který závisí na IUserRepository a IUserUseCasejak ve vnitřní vrstvě. Takže závislostí směr je správný, AFAIK.
miravelardo

Zde je diagram popisující závislost směrech: i.stack.imgur.com/1gHka.jpg
miravelardo

Jsem pochopit, co děláte, a tady je věc: 1. Správce nemusí mít vlastnost. Je to chybí na vás diagramu, buď to nenajdete v Čisté Architektury.2. Regulátor může být jednoduchá funkce. 3."Dostat uživatele správce"? Regulátor nebude provádět takové úzké funkce, to by mělo být jen "Správce". 4. Problémy začít s regulátorem vlastnost: odstranit. 5. Pochybuji o HTTP -> Správce toku, protože Čisté Architektury ukazuje HTTP -> UI/Presenter průtoku a regulátor zůstane stranou.
Alexander Fadeev

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................