string manipulation – ToString with delayed replacement

I want a toString method that replaces each occurrence of f(x_).

obj1ToString(x_) := StringRiffle(x, "");
obj2ToString(y_) := ToString(y /. f(x_) :> "(" <> obj1ToString@x <> ")");

obj2ToString(g@f({1, 2, 3}))
(* "g((123))" *)
obj2ToString(f({1, 2, 3}) &)
(* "(<>obj1ToString({1, 2, 3})<>) & " *)

As you can see, it doesn’t work in all contexts. How do I fix this?