Giter VIP home page Giter VIP logo

enum's Introduction

grifart/enum

repositories: GRIFART GitLab, GitHub

Enums represent predefined set of values. The available values are defined statically by each enum class. Each value is represented by an instance of this class in a flyweight manner.

  • This enum allows you to add individal behaviour to every enum value (as in Java). This allows you to transform your switches/ifs into more readable composition. (see example bellow)
  • Checks enum annotations if phpdoc-declared methods are properly declared (will generate docblock for you in exception)

Common with other enum implementations:

  • You can type-hint: function setCurrentDay(DayOfWeek $day) {
  • You can get a list of all the possible values

Let code speak: individual behaviour for each value

Let's refactor following existing code:

/**
 * @method static DayOfWeek MONDAY()
 * @method static DayOfWeek THURESDAY()
 */
final class DayOfWeek extends \Grifart\Enum\Enum
{
    use Grifart\Enum\AutoInstances;

    private const MONDAY = 'monday';
    private const THURESDAY = 'thuresday';
    // ...

}

$monday = DayOfWeek::MONDAY();

function nextDay(DayOfWeek $dayOfWeek): DayOfWeek
{
    if($dayOfWeek === DayOfWeek::MONDAY()) {
        return DayOfWeek::THURESDAY();
    } else if (...) {
        ...
    }

    throw new ShouldNotHappendException();
}

$thuresday = nextDay($monday);

Look at function nextDay(), it is really concerned with DayOfWeek and nothing else. It probably should be part of DayOfWeek. Let's try to push behaviour down.

/**
 * @method static DayOfWeek MONDAY()
 * @method static DayOfWeek THURESDAY()
 */
final class DayOfWeek extends \Grifart\Enum\Enum
{
    use Grifart\Enum\AutoInstances;

    private const MONDAY = 'monday';
    private const THURESDAY = 'thuresday';
    // ...

    public function nextDay(): self
    {
        if($this === self::MONDAY()) {
            return self::THURESDAY();
        } else if (...) {
            ...
        }

        throw new ShouldNotHappendException();
    }
    
}

Cool, lets try to use this enum in our application:

$monday = DayOfWeek::MONDAY();
$thuresday = $monday->nextDay();

Public API now looks much better! Asking monday, what is the next day and it knows answer. ✅

However I'm still worried about code of nextDay() function.

  • ❌ There are still ugly ifs which are hard to read.
  • ❌ I have to worry about case when someone adds new value to this enum and forgets to update nextDay() method.

Both of these can be solved by using composition instead of just value and switches.

What if every value of enum would be separate class? Then we can write behaviour for each day individually making code very simple. And if someone adds new value, type-system will force him to add all required behaviour. And grifart/enum makes this easy, you do not have declare separate class for every value, just use anonymous classes.

/**
 * @method static DayOfWeek MONDAY()
 * @method static DayOfWeek THURESDAY()
 */
abstract class DayOfWeek extends \Grifart\Enum\Enum
{

	protected const MONDAY = 'monday';
	protected const THURESDAY = 'thuresday';
    // ...

    public abstract function nextDay(): self;

    
    /** @return static[] */
	protected static function provideInstances(): array
	{
		return [
			self::MONDAY => new class extends DayOfWeek
			{
				public function nextDay(): DayOfWeek
				{
					return DayOfWeek::THURESDAY();
				}
			},

			self::THURESDAY => new class extends DayOfWeek
			{
				public function nextDay(): DayOfWeek
				{
					return return DayOfWeek::WEDNESDAY();
				}
			},
		];
	}
}

Now type-system knows that every enum value must have method nextDay() with return type of self ✅. Please note that this is completely internal thing - public API haven't changed! And we got rid of all ifs and switches ✅.

This approach is very useful when one wants to implement anything state-machine related (see tests for more examples, they are simple and easy to read).

More use cases:

  • order state (new, in progress, delivering, delivered) and relations between them
  • day of week
  • tracking life-cycle

More reading

enum's People

Contributors

jkuchar avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.