Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Interface Segregation (Future Version?) #198

Open
designermonkey opened this issue Nov 2, 2023 · 3 comments
Open

Better Interface Segregation (Future Version?) #198

designermonkey opened this issue Nov 2, 2023 · 3 comments

Comments

@designermonkey
Copy link

Hi @rtheunissen.

I use this extension every day, and in our current project, I have been creating wrappers for classes to handle concurrency with OpenSwoole. That's not the point of this, though ;)

While doing so, I've found it challenging to type hint my application classes to be able to handle the extension classes or my decorated concurrent replacements because I believe the interfaces provided aren't as good as they could be.

To try to help out, I've spent a couple of hours going through everything with a fine-toothed comb to see what would work better, as many classes share similar, if not the same, methods but with no interface backing them up.

I'm wondering if a v2 is on the horizon for this reason and also for better type hinting for PHP 8.x. What I have done I have added below for your review and criticism (everything is interfaces for ease of description).

I'm not formally qualified in computer science, so I might not fully understand the data structures. I am also not a C programmer, so I wouldn't be able to offer any coding help with a new version, though I wish I could.

Anyway, please let me know what you think and whether there will be a v2.

<?php declare(strict_types=1);

namespace Ds;

use ArrayAccess;
use Countable;
use IteratorAggregate;
use JsonSerializable;

/**
 * Original Hierarchy:
 *
 * Hashable
 * Pair
 * Collection
 *   Sequence
 *     Deque
 *     Vector
 *   Map
 *   Set
 *   Stack
 *   Queue
 *   PriorityQueue
 */


/**
 * Proposed Hierarchy:
 *
 * Equatable
 * Hashable
 * Pair
 * DataStructure
 *   Collection
 *     TransformableCollection
 *       Set
 *       Map
 *     Sequence
 *       Deque
 *       Vector
 *   LinearSequence
 *     Stack
 *     Queue
 *     PriorityQueue
 */


interface DataStructure extends Countable, IteratorAggregate, JsonSerializable
{
    public function clear(): void;
    public function copy(): DataStructure;
    public function isEmpty(): bool;
    public function toArray(): array;
    public function allocate(int $capacity): void;
    public function capacity(): int;
}

interface Pair extends JsonSerializable
{
    public function clear(): void;
    public function copy(): Pair;
    public function isEmpty(): bool;
    public function toArray(): array;
}

interface Equatable
{
    public function equals(object $obj): bool;
}

interface Hashable extends Equatable
{
    public function hash(): mixed;
}

interface Collection extends DataStructure
{
    public function filter(callable $callback = null): Collection;
    public function first(): mixed;
    public function last(): mixed;
    public function merge(mixed $values): Collection;
    public function reduce(callable $callback, mixed $initial = null): mixed;
    public function reverse(): void;
    public function reversed(): Collection;
    public function slice(int $index, int $length = null): Collection;
    public function sort(callable $comparator = null): void;
    public function sorted(callable $comparator = null): Collection;
    public function sum(): int|float;
}

interface TransformableCollection extends Collection
{
    public function diff(Collection $map): Collection;
    public function intersect(Collection $map): Collection;
    public function union(Collection $map): Collection;
    public function xor(Collection $map): Collection;
}

interface Set extends TransformableCollection, ArrayAccess
{
    public function add(mixed ...$values): void;
    public function remove(mixed ...$values): void;
    public function contains(mixed ...$values): bool;
    public function get(int $index): mixed;
    public function join(string $glue = ''): string;
}

interface Map extends TransformableCollection, ArrayAccess
{
    public function apply(callable $callback): void;
    public function get(mixed $key, mixed $default = null): mixed;
    public function hasKey(mixed $key): bool;
    public function hasValue(mixed $value): bool;
    public function keys(): Set;
    public function ksort(callable $comparator = null): void;
    public function ksorted(callable $comparator = null): Map;
    public function map(callable $callback): Map;
    public function pairs(): Set;
    public function put(mixed $key, mixed $value): void;
    public function putAll(mixed $pairs): void;
    public function remove(mixed $key, mixed $default = null): mixed;
    public function skip(int $position): Pair;
    public function values(): Sequence;
}

interface Sequence extends Collection
{
    public function apply(callable $callback): void;
    public function contains(mixed ...$values): bool;
    public function find(mixed $value): mixed;
    public function get(int $index): mixed;
    public function insert(int $index, mixed ...$values): void;
    public function join(string $glue = ''): string;
    public function map(callable $callback): Sequence;
    public function remove(int $index): mixed;
    public function rotate(int $rotations): void;
    public function set(int $index, mixed $value): void;
    public function shift(): mixed;
    public function unshift(mixed $values = null): void;
}

interface Deque extends Sequence, ArrayAccess
{
}

interface Vector extends Sequence, ArrayAccess
{
}


interface LinearSequence extends DataStructure
{
    public function peek(): mixed;
    public function pop(): mixed;
    public function push(mixed ...$values): void;
}

interface Stack extends LinearSequence, ArrayAccess
{
}

interface Queue extends LinearSequence, ArrayAccess
{
}

interface PriorityQueue extends LinearSequence
{
    /** Using push(mixed ...$values) would add items as `0` priority */
    public function pushPriority(mixed $value, int $priority): void;
}
@rtheunissen
Copy link
Member

This is great, thank you for taking the time.

I have a draft API sketched out for a v2 that I am very eager to get around to. I think as part of that, when all is said and done, will be a push to either merge into core or push for default extension like json.

Could you maybe justify some of the design decisions here? I would like to know why the hierarchy is the way it is, to better understand your experience and use that to inform the next phase.

Once I'm happy with the API I'll post it for feedback and hopefully arrive at some consensus to then move forward with implementation and testing.

@designermonkey
Copy link
Author

Could you maybe justify some of the design decisions here? I would like to know why the hierarchy is the way it is

That's a good question. I guess I wanted to group the methods on each class a little better. That being said, it still doesn't help my actual use case, which I will explain...

We are using Swoole, and I wanted to create a ConcurrentMap class to allow proper memory protection. I saw that there wasn't an interface I could use to allow the use of Ds\Map or my own ConcurrentMap interchangeably, so I tried to figure out what the commonalities were and came up with the above.

As I point out, this is still not helping as I still can't see an interface that allows me to decorate your Ds\Map with my ConcurrentMap and use methods like put and get etc as they are unique to Map, so back to the design phase for my classes for me :)

@designermonkey
Copy link
Author

designermonkey commented Nov 16, 2023

I just realised after I wrote that, that I can use ArrayAccess. I've never been a fan of it as I prefer objects, but it is what it is I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants