1

I have two classes, Repository and Search. Repository encapsulates the logic for storing and managing the content, and Search encapsulates the logic for searching through the content and also requires access to the Repository object, in order to read the content.

Generally, I want to divide the functionality of Repository class into public, that every user of the class will have access to, and private, which should only be available internally. The problem I have is that Search class should have access to the private functionality of Repository but because the Repository object will passed to Search object from the outside, it will only expose public functionality.

The way I am doing it right now, is that Repository uses pimpl idiom, to hide implementation details, and I declare Search as friend, so it can use the private functionality in implementation code.

Repository.h:

// Forward declaration of implementation class
class RepositoryImpl;

// Forward declaration of friend class
class Search;

class Repository
{
public:
   // public interface

private:
   RepositoryImpl* pImpl;
};

Search.h

class Search
{
public:
   void searchRepository(Repository* repo);
};

Search.cpp

void Search::searchRepository(Repository* repo)
{
   repo->pImpl->someHiddenMethod();
}

This obviously is just to give and idea so it does not make any sense in terms of actual functionality but I hope it explains well what I am struggling with. Some might say that using friend here is perfectly fine, but to me it feels a bit "hacky" so if there is a better way (some design pattern) to go about it, I would like to use.

I also don't want to merge those classes into one (even though it might seem like a good idea) because it introduces certain limitations that are not acceptable. Basically I want to be able to have one search engine, being able to work with multiple repositories. I am open though to complete redesign of the classes if it will allow to achieve the goal in clean manner.

Thank you very much in advance.

zedsdead
  • 51
  • 6
  • 2
    Why not make that `someHiddenMethod` public and just access it from public interface? – Yusuf Tarık Günaydın Dec 01 '15 at 16:18
  • 1
    Making `Search` a `friend` of `Repository` is definitely hackish. It is a symptom of inadequate `public` interface of `Repository`. I think it will be better to provide access to *all the contents* of `Repository` in the `public` interface. Then there won't be the need for granting `friend`ship to `Search`. – R Sahu Dec 01 '15 at 16:18
  • https://stackoverflow.com/questions/12048193/why-is-it-legal-to-inappropriately-access-privates-in-an-explicit-instantiation – David Dec 01 '15 at 16:52
  • qqww2 someHiddenMethod will basically allow to retrieve some information from repository that I don't want to make available to the user. At least not directly. – zedsdead Dec 01 '15 at 17:23

2 Answers2

2

You can create an interface, say Searchable, that Search uses and that RepositoryImpl implements. Then, add a public interface to Repository that returns the pImpl member but narrowed to the Searchable interface.

The advantage of doing the above is that you do not have to expose all of the implementation details of RepositoryImpl to Search, which is what makes your approach feel like a hack.

// in Search.h
class Searchable
{
    friend class Search;
    virtual void internal_method () = 0;
    //...
protected:
    virtual ~Searchable () = default;
};

// in Repository.cpp
class RepositoryImpl : public Searchable //...
{
    //...
};

Searchable * Repository::searchable () { return pImpl; }

// in Search.cpp
void Search::searchRepository(Repository* repo)
{
   repo->searchable()->internal_method();
}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Your solution is basically what was done initially. In order to explain why it was not a good solution I will try to describe the problem slightly better. When the content is put into repository, it is divided into chunks but the user knows nothing about it. When they request the data out, they get it in one piece and merging (if required) happens behind the scene. Search algorithm requires an access to those chunks, which is implemented as private methods. If I expose the interface, I will also expose the information about underlying structure, which I don't want to do. [continued below] – zedsdead Dec 02 '15 at 13:09
  • I was considering forward declaring Searchable, so the pointer could be passed to Search class, without revealing what it does, but to me it feels even less elegant then using friend, since I expose a pointer to incomplete type. – zedsdead Dec 02 '15 at 13:10
  • Exposing the components required by `Search` is exactly the job of `Searchable`. Note how the methods of `Searchable` are private, and can only be accessed by its friend `Search`. – jxh Dec 02 '15 at 20:19
0

Probably you don't need to have access to the Repository implementation details.

Search should be able to work on Repository without knowing anything about Repository's details like if you were using a STL container and iterators to its elements.

So, probably you should modify Repository interface in order to grant access to the underlying elements (like adding begin() and end() to the public interface) and let the Search class to work with these "handles". Better if Search could be generic enough to work with other kinds of "iterators" too.

P.S. I would have preferred to add this as a comment, because the informations are not as much accurate to give a definitive answer. But I'm not allowed because of my low reputation.

Emilio
  • 21
  • 2
  • as I mentioned in the response to the comment, RepositoryImpl allows to access some very specific information directly and I definitely don't want to let the user access them. Search class requires them only internally, in order to perform more complicated algorithms. I appreciate what you are saying, that Repository public interface should be sufficient for anything using it, but at the current state it would require re-implementing both classes fundamentally, which I cannot afford at the moment. – zedsdead Dec 01 '15 at 17:30