1

I have to create a class named Phone, which can be any phone like Android, iOS and Windows. Again in Android, I will have various types of phones. For me challenge is, I have to maintain more than 100 properties of each phone. Some features are common in all the three phones, some are common between only Android and iOS not in Windows. Some features in Android may get added in iOS(vice versa) in future. Some features in iOS can be deprecated. How should I design by following Open-close principle? If I go by strategy design pattern, I have to create interface for each property, which I feel doesn't make sense.

Can some one guide what is the best way to create Phone class?

  • 1
    Create a Phone class containing all of the common properties, and then create AndroidPhone, ApplePhone and WindowsPhone classes that inherit from the Phone class? – Robert Harvey Jun 25 at 23:27
  • Are these "data" or are these "behaviors" ? – rwong Jun 27 at 22:13
  • 1
    Considering that you have added the C++ tag, if in case it becomes necessary to ship new "behavior" (i.e. new C++ compiled code ("binary") containing that new behavior or changes), you might want to consider a plugin approach, dividing functionality into "shared objects" (".so") or dynamic libraries. – rwong Jun 27 at 22:15
  • Most of them are datas. I have only 1 or 2 behaviors now. – Yash Jun 29 at 22:21
3

HAL

Hardware Abstraction Layer. It is a big part of the Windows OS (with similar analogues in most OSes) which has to interface with many different CPUs, Motherboards, an other periphery devices.

This layer augments the underlying hardware so that each of these things looks identical on the outside. Where the the hardware falls short of providing the functionality, it is instead implemented by one or more software implementations requiring some subset of the service from hardware.

An Implementation

  1. Establish a common interface with all of the goodies expected out of the phone.

  2. Create a Phone features object with simple booleans or as an enumeration/integer to allow a nuanced selection, eg hasFeatureX = true, maxScreenWidth = 512, interfaceStyle = blue_black

  3. Write phone specific objects implementing the interface, but which error or do nothing on unsupported features.

  4. Write a generic object which takes the Phone features and a specific phone object and chooses to call through, or perform the work itself based on the phone features.

This will look something like:

interface Phone
{
   doX(...);
...
}
class PhoneFeatures
{
   bool hasFeatureX;
   int maxScreenWidth;
   Style interfaceStyle;
}
PhoneFeatures iPhoneXYZFeatures = new PhoneFeatures 
{
   hasFeatureX = true,
   maxScreenWidth = 512,
   interfaceStyle = blue_black
}
class PhoneHAL : Phone
{
   PhoneHAL(PhoneFeatures feature, Phone phone);


   doX(...)
   {
       if (feature.hasFeatureX)
          phone.doX(...);
       else if (feature.hasFeatureY && feature.hasFeatureZ)
       {
          ...some algorithm using Y and Z to accomplish X...
       }
       else
          throw "shouldn't get here but otherwise an informative message."
   }
}
class IPhone : Phone
{
    ...expose hardware implementation, but nothing else...
}

void main()
{
    Phone phone = new PhoneHAL(iPhoneXYZFeatures, new IPhone());
}

Its possible to share a Phone implementation between several versions of the phone and use the Features to use or avoid the platform implementation.

For example perhaps the scroll bar provided in version X.Y.Z is broken, you can still use the same underlying mapping for the phone, but set the feature toggle to false.

This is one way of implementing the idea, there are other ways to achieve the same effect.

| improve this answer | |
  • Thanks a lot for your response and time for answering. If I have to add new feature, I have to modify interface again. It is against open-close principle. For me it is really hard to create a class by satisfying all these principles. – Yash Jun 26 at 2:50
  • Then implement the Phone interface as an assembly of interfaces. eg: Screen, GPSv1, etc... Use a tagging interface like RawPhone as the base for all phone implementations, with some universal attributes (like OSName). Update the Features with controlling attributes, but these can be reasonably defaulted. This way only the HAL object (implementing Phone), and those actual raw phone objects (implementing RawPhone + NewFeatureSet) need to be extended. You can even refactor this and provide the functionality as per an Entity Component System for even better open-closed. – Kain0_0 Jun 26 at 4:01
  • 1
    @Yash You don’t need to satisfy all the principles all the time, especially not when we’re talking about SOLID. They’re no dogma – although they’re often presented that way –, they’re tools for making designing your software easier. Rather think of SOLID as a checklist of things to consider. Concluding that for your current piece of code following principle X is not useful is a perfectly valid result. – besc Jun 26 at 8:10
1

You already got a good answer, but let me focus on this question of yours:

How should I design by following Open-close principle

The answer is: you don't. The OCP is not an end in itself, it is a means to an end. It would make sense if you are a framework designer who wants to provide a generic library for other developers who buy this closed-source lib from you to and use it in applications for several unforeseen phone types. This is, to my understanding, not your situation.

Currently, you are aiming for supporting 3 phone types. Maybe there will be a new one next year, with additional new properties none of the former three provided - to support it, you will have to extend your existing interfaces, their is no way around it.

That does not mean you cannot achieve the OCP in parts, by segregating interfaces, like suggested by @Kain0_0 in a comment under his answer. But to make use of new features of a phone, you will have at least add a new interface (or extend one of the existing) and add code which makes use of the new interface.

| improve this answer | |
1

I am thinking to design as below, Create a class named Phone which contains 2 types of objects. one basic properties and other is advanced properties. BasicProperties class contains all the properties without which we can not call phone.(OS type, screen size, RAM size, battery level,sim card support). These will be common among all the phones and these should not be changed in future. All the advanced features will go to AdvancedProperties class, which can contain all the properties which can/cannot be common among all the phones. At least by this I can limit changes to AdvancedProperties class. Andriod/iOS/Windows phones are inherited from Phone class.

  1. If any features added to only Android phone: Go and modify AdvancedProperties class and by default set those properties to false. Set property true in Android phone.
  2. If any other properties are implemented in iOS which are in Android: Set property true in iOS phone and for android it is already true.
  3. If any of the property is deprecated in all the three phones Ex:2G network support Set that property by default to false. Don't set this property in all the three phone types.

Thanks for your help. I am really for the help I got in this forum.

| improve this answer | |

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.