1
votes

Quelle est la bonne pratique lorsque vous essayez d'accéder aux méthodes enfants à partir d'un objet de type statique parent?

J'ai un problème d'héritage. Je l'ai illustré avec ces classes de comptes:

Account account = new AdminAccount();
if(account.accountLevel == ADMIN){
    AdminAccount adminAccount = (AdminAccount) account;
    adminAccount.addProduct();
}

Si j'ai une instance d'un compte et que je souhaite la convertir en AdminAccount pour faire addProduct (), est-ce que je peux le faire:

class Account {
    accountLevel = BASIC;
    connect() {...}
    changePassword() { ... }
    ChangeEmail() { ... }
}

class CustomerAccount extends Account {
    accountLevel = CUSTOMER;
    createOrder() { ... }
    payOrder() { ... }
}

class AdminAccount extends Account {
    accountLevel = ADMIN;
    addProduct() { ... }
    deleteProduct() { ... }
}

Modifier: Je souhaite que tous mes comptes soient dans la même liste, car ils utilisent tous la même méthode de connexion. Mais lorsque le compte est connecté, j'ai toujours un objet de compte mais je souhaite utiliser leurs méthodes de type dynamique, je suis donc obligé de lancer l'objet.

C'est presque comme une instanceof mais avec des étapes supplémentaires. J'ai l'impression d'avoir à lancer le compte n'est pas très OOP, existe-t-il une autre solution plus élégante? Peut-être pas utiliser l'héritage entre les classes de comptes?


4 commentaires

Pourquoi essayez-vous différents types de comptes de manière générique? En général, seul le code qui effectue des opérations générales reçoit des instances de type de base, tandis que les méthodes plus spécialisées reçoivent les instances spécifiques


Alors recommanderiez-vous de n'avoir qu'une seule classe Account avec toutes les méthodes. Et ils pourraient envoyer une exception si le niveau d'accès n'est pas assez élevé? De cette façon, il n'y a pas de problème d'héritage, mais est-ce moins oop?


En général, j'essaie d'éviter l'héritage. Au fil du temps, cela gâche vraiment tout et rend chaque changement plus difficile. Mais ce n'est qu'une opinion. Vous trouverez d'autres opinions là-bas, et elles sont toutes aussi valables que les miennes. Au final, c'est une question de goût


Vous avez donc tous les comptes mélangés dans une liste. Pourquoi? Comment sont-ils arrivés là-bas? Vous voudrez peut-être jeter un coup d'œil au modèle de visiteur, c'est la bonne façon d'éviter si les instructions par un attribut de type ou une instance de


3 Réponses :


1
votes

Une façon d'aborder cela est:

    public static void main(String args[])
    {
        AccountManager manager = new AccountManager();
        manager.addAccount(new User());
        manager.addAccount(new User());
        manager.addAccount(new Admin());
        System.out.println(manager.getUsers().collect(Collectors.toList()));
    }

    public static final class AccountManager
    {
        private final List<Account> allAccounts = new ArrayList<>();
        public Stream<User> getUsers(){ return getAccountType(User.class); }
        public Stream<Admin> getAdmins(){ return getAccountType(Admin.class); }
        public Stream<Account> getAccounts(){ return getAccountType(Account.class); }
        public <T extends Account> void addAccount(T account) { if(account.connect()) allAccounts.add(account); }
        @SuppressWarnings("unchecked") private <T> Stream<T> getAccountType(Class<T> type){ return allAccounts.stream().filter(type::isInstance).map(act -> (T) act); }
    }

    public static final class User extends Account{}
    public static final class Admin extends Account{}
    public static class Account { boolean connect(){ return true; } }

Si l'attente est que seul un compte administrateur peut ajouter un produit, les méthodes pertinentes doivent s'attendre à un compte administrateur dans l'entrée au lieu d'un compte. Y a-t-il une raison pour laquelle l'instance est créée en tant que compte au lieu d'un compte administrateur?

Modifier - Inclure un exemple de gestion de type d'emballage dans une classe de gestionnaire.

AdminAccount account = new AdminAccount();
account.addProduct();


2 commentaires

Je souhaite que tous mes comptes soient dans la même liste, car ils utilisent tous la même méthode de connexion. Mais lorsque le compte est connecté et que je souhaite utiliser leurs méthodes de type dynamique, je suis obligé de convertir l'objet. Cela ne semble pas être très oop


Cela peut valoir la peine de mettre à jour la question avec ces détails. Une option pour résoudre ce problème consiste à ajouter une classe AccountManager qui gère chacun des types de compte. Il peut être acceptable de convertir en un sous-type, mais en général, cette logique doit être empaquetée pour éviter toute utilisation abusive. Y compris un échantillon ci-dessus.



0
votes

C'est vraiment OK. Nous l'appelons polymorphisme en POO. Les parents peuvent être jetés à tous les enfants.


0 commentaires

0
votes

En général, il n'est pas considéré comme une bonne pratique d'avoir des instructions if par un attribut de type ou par instanceof checks.

Une alternative à cela est le modèle de visiteur . Voici un aperçu de ce que vous pourriez faire:

class AccountProcessorImpl implements AccountProcessor {

    private List<Account> accounts; // filled with all the accounts

    void doSomethingWithAllTheAccounts() {

        // iterate all the accounts and process each one of them
        accounts.forEach(IAccount::process);
    }

    @Override
    public void processBasicAccount(Account acc) {
       // do something with the basic account
    }

    @Override
    public void processCustomerAccount(CustomerAccount acc) {
       // do something with the customer account
    }

    @Override
    public void processAdminAccount(AdminAccount acc) {
       // do something with the admin account
    }
}

L'idée est que vous avez une interface qui définit une méthode de process pour tous les comptes, qui reçoit un AccountProcessor (vous pouvez changer les noms ici, c'est juste pour montrer un exemple). Il devrait y avoir une méthode pour chaque type de compte. Vous pouvez même utiliser la surcharge de méthode , c'est-à-dire que toutes les méthodes peuvent être nommées quelque chose comme processAccount et recevoir un type de compte spécialisé.

Maintenant, votre classe Account et tous ses sous-types doivent implémenter l'interface IAccount :

class Account implements IAccount {

    @Override
    void process(AccountProcessor processor) {
       processor.processBasicAccount(this);
    }

    // all the other Account stuff
}

class CustomerAccount implements IAccount {

    @Override
    void process(AccountProcessor processor) {
       processor.processCustomerAccount(this);
    }

    // all the other CustomerAccount stuff
}

class AdminAccount implements IAccount {

    @Override
    void process(AccountProcessor processor) {
       processor.processAdminAccount(this);
    }

    // all the other AdminAccount stuff
}

Maintenant, avec toutes ces pièces en place, vous êtes prêt à traiter votre liste de comptes:

interface IAccount {
    void process(AccountProcessor processor);
}

interface AccountProcessor {

    void processBasicAccount(Account acc);

    void processCustomerAccount(CustomerAccount acc);

    void processAdminAccount(AdminAccount acc);
}

J'espère que vous comprenez l'idée générale. Ceci est juste un croquis pour vous montrer la technique de base. Il peut y avoir des variantes, c'est-à-dire que vous pouvez avoir des processeurs distincts pour chaque type de compte différent, ou vous pouvez traiter la liste des comptes dans une classe autre que AccountProcessorImpl , etc.


2 commentaires

Mais cela voudrait dire que j'ai des méthodes sans rien. Ne serait-ce pas aussi un problème?


@Ateatwe Vous devez implémenter les méthodes