4

this question is actually about a complex java project I am doing at university, but it is possible to find a simple case which is really similar to the problem I am facing.

In this scenario, we have an app with free version and premium version. A user registers the app for the first time and uses the free version by default. If he wants to do so, he can pay and become a premium user. I want to emphasize the fact that the premium version "doesn't last forever", so it is possible that he goes back to his free-user status. (this may seem strange, but it is due to the fact that this isn't really my study case).

It is also important to remark that there are some features / actions that are common to both free and premium user, but ther are some that can't be shared. For example, a premium user cannot have a method to pay to become a premium user (which is instead available to the free user).

The first thing that came to my mind was using inheritance, since both free and premium user were users, but how to achieve that the same user could switch between those two roles?

Then I tought about using the State Pattern, but there is still something that doesn't seem quite right to me. If I understand it right, I would have to declare all the methods (not only the "shared ones", but also the methods which concern only the freeUser or the premiumUser) in the interface, and then throw exceptions if an illegal call was made (as I said before, if a premium user tried to pay for premum version). But at least this solution allows easy switching between the two roles.

It seems to me that this is a quite common problem nowadays, but I couldn't find anything relevant and good enough on the internet.

  • 3
    Design patterns are not building blocks. – whatsisname Oct 20 '16 at 16:29
  • Yeah, "design pattern" doesn't mean what you think it means. What you're describing is a design. Every software has one. – Robert Harvey Oct 21 '16 at 0:18
1

Clarify your requirements

Which of the following do you intend to implement ?

  • premium/free users (as suggested in the title): you have an app with a role based design. One instance of the app could have several users. The same app delivers different behavior to different users (depending on their active role).
  • premium/free app (you wrote "we have an app with free version and premium version"): one instance of the app delivers the same behavior for all its users at a given time. The premimum behavior can be temporarily activated but is the same for all users.
  • premimum/free service (you wrote "premium user cannot have a method to pay to become a premium user"): you expose an API or you provide a library that wrapps the access to the API, so that you are realy concerned about internal interface.

How to implement

The state pattern as you already found out seems the most appropriate answer to all the case. Depending on your requirements, you can use a user state or an app state.

If you offer a library wrapper to a service, then it may matter to offer only methods relevant for the state. But this makes life difficult for your library's : he'd first need to query the state, and then has to know which state allows which method. That's terrible: you'll end-up having a lot of undesired dependencies in the client code. Therefore I would offer the same interface for all the states, but add some methods to know if some features are available or not which could be called before invoking the method.

If you offer a user interface and not an API, you have to properly act on user request. So if a feature is no longer available, gray it out in menus, or do not provide the button or event handling for it. Using a state dependent interface would make user interface consistency difficult. I'd really rather recommend to use the same methods in all the states (eventually generating messages like "Feature not available. You need to purchase the feature...") and if needed provide functions like hasFeatureX() to help the app to decide. This would be far more maintenable: if you'd introduce new paid features (with a different price), or if you'd had special roles (e.g. "Auditor", "Admin", etc...) it would be fare more easy to define which state allows access to which feature.

Finally, you could think of using strategy pattern(again depending on user or on app state). But strategies are not states, so this approach would not work if you have some things to be performed for ensuring proper state transition. It works only if the algorithms are really interchangeable.

  • Thank you for your answer, I found it really helpful! I think that in my case I have a premium/free app. The switch between the two states is toggled by the user, but it is the same for every free/premium user. Because of this, I thought about offering a user interface, in which certain features could become (un)available depending on the state of the user. But it seems more appropriate now to offer the same interface to both types of user, and prompt him to change state if he wants to access that service / feature. – Giada S Oct 21 '16 at 1:24
  • 1
    @GiadaS good idea. The ui could still be an observer of the app state. You have the choice of the best way to advertise premium functionality: let menus be there and provide a popup, or deactivate premium features if !appState.isPremium() but puting info about premium requirement in the tooltip/hoover text – Christophe Oct 21 '16 at 7:06
4

Leaving aside named patterns for a moment, I would start with something like this.

Simple Class Structure for Users, Features and Subscriptions

The general idea here is that your user has a subscription, which has a subscription level. The subscription object can include a time range (allowing for expiration to be checked).

Based on the rest of your model, you would create a service that returns answers about whether or not a user has access to a given feature based on their subscription.

  • Thanks for your reply. I have two questions about what you suggested. First thing, the subscription doesn't expire by itself, but is the user who can choose to be either a free user or a premium user (in my particular domain). Would it still be okay with the solution you suggested? And apart from that, where should I write the methods which are dependent on the subscription status? – Giada S Oct 20 '16 at 15:48
  • The meat of the idea should work either way. The start / end date came from the statement that the premium version didn't last forever. As for where to put the methods, it depends on the details of your use-case and how complex your logic is. I would probably start with a service/Business Logic class that has a method that returns whether or not a user has access to a feature by looking up the required level in some sort of table. – Michael Oct 20 '16 at 15:51
  • I'll try that as soon as I can. Thank you very much! – Giada S Oct 20 '16 at 16:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.